Comments 18
Подмешивание интерфейсов рулит.
А так, да — множественное наследование иногда выдает крайне забавные эффекты, причем часто на рантайме.
Помнится, в MFC наследование от CObject не одну свинью подложило…
А так, да — множественное наследование иногда выдает крайне забавные эффекты, причем часто на рантайме.
Помнится, в MFC наследование от CObject не одну свинью подложило…
0
«They that can give up essential liberty to purchase a little temporary safety, deserve neither liberty nor safety» Бенджамин Франклин.
Множественное наследование очень мощный инструмент. Более того я не представляю насколько хреново пришлось бы разработчикам Qt и пищущих на нем, если один из стандартов С++ запретит множественное наследование.
Просто разработчики часто путают геометрическую схожесть с сущностной, от сюда все проблемы с наследованием.
Множественное наследование очень мощный инструмент. Более того я не представляю насколько хреново пришлось бы разработчикам Qt и пищущих на нем, если один из стандартов С++ запретит множественное наследование.
Просто разработчики часто путают геометрическую схожесть с сущностной, от сюда все проблемы с наследованием.
+11
Ну почему же, вот в Scala с миксинами довольно аккуратно все сделано, и порядок вызова родительских методов математически формализован (linear mixin composition)
+1
macton.smugmug.com/gallery/8936708_T6zQX#!i=593426709&k=ZX4pZ
0
JustVisiblePlusUpdate(bool visible, bool active)
: JustVisible(visible)
, VisualActivity(visible, active)
, Renderable(visible)
, Updatable(active)
{
}
Воу, сколько раз будет выполнен конструктор Renderable, 1 или 3 раза?
0
Да вот и я удивился — один раз
0
Если это геймдев и смежные области (реалтайм компьютерная графика), то вместо такой архитектуры со временем приходят к Data Driven и Data Oriented подходам. А когда к ним приходят то Object as a pure aggregation самое оно. По сути — у нас уже нету никаких объектов, есть только массивы (часто некоторые типы наподобе позиции разбивают по елементам на отдельные массивы заради SSE оптимизаций — SoA vs AoS), и в этих массивах содержатся примитивные елементы: updatable, renderable и прочие. Связи между елементами — data driven и задаются извне.
+6
Компонентно ориентированное программирование как раз избавляет от граблей множественного наследование и предоставляет механизм равнозначный подмешиванию интерфейсов.
0
В нашем случае было принято решение отказаться от разделения на Renderable и Updatable, ограничившись одним базовым VisualActivity. Это добавило некоторую избыточность
Это же ужасно. Стоило бы просто отказаться от наследования реализации, заменить её аггрегацией и множественным наследованием «интерфейсов».
+1
По-моему, тут просто оверинжиниринг и грабли именно в этом, а не в виртуальном наследовании. С++, конечно, дает себе в ногу выстрелить в этом плане, но зачем это делать? Непонятно, зачем вообще усложнять модель и разделять Renderable и Updatable. Во-первых, кода на этом мы ненаэкономим. Во-вторых, скорее всего, и в читаемости ничего не выиграем, бедным программистам контролов просто надо будет писать больше кода.
Тут вполне можно обойтись одним классом типа Widget и от него наследоваться. «Фасад» он будет или просто тупой как валенок, это уже другой вопрос. По идее, если грамотно итерфейсы использовать, тогда двойное наследование не должно быть нужно вообще. В случае когда один контрол явлется одновременно двумя другими ИМХО лучше использовать составные контролы-контейнеры.
Еще (судя по тому что я видел/писал) чаще всего виртуально наследуются от единственного класса на весь API, тогда меньше путаницы. Но даже этого, как оказывалось, проще избегать, чем мучаться потом.
Тут вполне можно обойтись одним классом типа Widget и от него наследоваться. «Фасад» он будет или просто тупой как валенок, это уже другой вопрос. По идее, если грамотно итерфейсы использовать, тогда двойное наследование не должно быть нужно вообще. В случае когда один контрол явлется одновременно двумя другими ИМХО лучше использовать составные контролы-контейнеры.
Еще (судя по тому что я видел/писал) чаще всего виртуально наследуются от единственного класса на весь API, тогда меньше путаницы. Но даже этого, как оказывалось, проще избегать, чем мучаться потом.
+2
Вот и грабли просто дают возможность получить по лбу, а уж наступает сам человек. Ясное дело, что само по себе виртуальное наследование ни в чем виноватым быть не может. Цель статьи — предостеречь от неудачного проектного решения, а не обвинить язык.
+1
Так грабли там положили гораздо раньше, когда классов лишних придумали там, где они не нужны. Наступили, да, потом :) Если б статья называлась «Грабли 2: лишние сущности в коде», то вопросов бы не было.
Тут не то, чтобы дело в том, что надо отказываться от развесистой диаграммы классов, тк она в С++ (Java, ...) плохо ложится. А скорее то, что развесистая диаграмма классов — это слишком сложная модель в принципе, в ней много лишнего, как ее не реализуй.
Тут не то, чтобы дело в том, что надо отказываться от развесистой диаграммы классов, тк она в С++ (Java, ...) плохо ложится. А скорее то, что развесистая диаграмма классов — это слишком сложная модель в принципе, в ней много лишнего, как ее не реализуй.
+2
Полностью согласен. Количество случаев, когда появляется желание использовать множественное наследование монотонно убывает с ростом опыта.
По сути мне в последнее время кажется, что множественное наследование не-интерфейсов полезно только при реализации низкоуровневых библиотечных механизмов, скрытых от пользователя библиотеки. Пример: наследование механизма счетчиков ссылок при (одном из вариантов) реализации умных указателей.
По сути мне в последнее время кажется, что множественное наследование не-интерфейсов полезно только при реализации низкоуровневых библиотечных механизмов, скрытых от пользователя библиотеки. Пример: наследование механизма счетчиков ссылок при (одном из вариантов) реализации умных указателей.
+1
Но рассмотрим подробнее, откуда взялось предположение, что приведенный код должен приводить к вызову конструктора Renderable::Renderable(bool visible) вместо Renderable::Renderable().
От незнания языка С++? :)
+3
Вы (вроде бы) забыли рассказать про одну важную проблему, связанную с виртуальным наследованием. Вариант наследования (обычное/виртуальное) выбирается при описании наследника, поэтому нельзя описать класс таким образом, чтобы гарантировать, что все его наследники унаследуются от него виртуально. Решается с помощью простой идиомы:
class ancestor_imp { // implementation of ancestor }; class ancestor : virtual public ancestor_imp { // empty dummy class };
+1
Sign up to leave a comment.
Грабли 2: Виртуальное наследование