Pull to refresh

Comments 18

Давно пора запретить множественное наследование. Описанные в топике неоднозначности только создают головную боль для всех.
Лучше использовать Фасад.

Ох и отхвачу ж я сейчас
Подмешивание интерфейсов рулит.

А так, да — множественное наследование иногда выдает крайне забавные эффекты, причем часто на рантайме.
Помнится, в MFC наследование от CObject не одну свинью подложило…
«They that can give up essential liberty to purchase a little temporary safety, deserve neither liberty nor safety» Бенджамин Франклин.
Множественное наследование очень мощный инструмент. Более того я не представляю насколько хреново пришлось бы разработчикам Qt и пищущих на нем, если один из стандартов С++ запретит множественное наследование.

Просто разработчики часто путают геометрическую схожесть с сущностной, от сюда все проблемы с наследованием.

Ну почему же, вот в Scala с миксинами довольно аккуратно все сделано, и порядок вызова родительских методов математически формализован (linear mixin composition)
    JustVisiblePlusUpdate(bool visible, bool active)
        : JustVisible(visible)
        , VisualActivity(visible, active)
        , Renderable(visible)
        , Updatable(active)
    {
    }

Воу, сколько раз будет выполнен конструктор Renderable, 1 или 3 раза?
Да вот и я удивился — один раз
Удивляться нечему. Ведь виртуальное наследование для того и нужно, что бы наследник имел только одну версию общего предка.
Печаль настанет тогда когда выяснится, что JustVisible и VisualActivity сильно зависимы от данных Renderable.
Я имел в виду удивление в первый раз) Потом-то дошло…
Если это геймдев и смежные области (реалтайм компьютерная графика), то вместо такой архитектуры со временем приходят к Data Driven и Data Oriented подходам. А когда к ним приходят то Object as a pure aggregation самое оно. По сути — у нас уже нету никаких объектов, есть только массивы (часто некоторые типы наподобе позиции разбивают по елементам на отдельные массивы заради SSE оптимизаций — SoA vs AoS), и в этих массивах содержатся примитивные елементы: updatable, renderable и прочие. Связи между елементами — data driven и задаются извне.
Компонентно ориентированное программирование как раз избавляет от граблей множественного наследование и предоставляет механизм равнозначный подмешиванию интерфейсов.
В нашем случае было принято решение отказаться от разделения на Renderable и Updatable, ограничившись одним базовым VisualActivity. Это добавило некоторую избыточность

Это же ужасно. Стоило бы просто отказаться от наследования реализации, заменить её аггрегацией и множественным наследованием «интерфейсов».
По-моему, тут просто оверинжиниринг и грабли именно в этом, а не в виртуальном наследовании. С++, конечно, дает себе в ногу выстрелить в этом плане, но зачем это делать? Непонятно, зачем вообще усложнять модель и разделять Renderable и Updatable. Во-первых, кода на этом мы ненаэкономим. Во-вторых, скорее всего, и в читаемости ничего не выиграем, бедным программистам контролов просто надо будет писать больше кода.

Тут вполне можно обойтись одним классом типа Widget и от него наследоваться. «Фасад» он будет или просто тупой как валенок, это уже другой вопрос. По идее, если грамотно итерфейсы использовать, тогда двойное наследование не должно быть нужно вообще. В случае когда один контрол явлется одновременно двумя другими ИМХО лучше использовать составные контролы-контейнеры.

Еще (судя по тому что я видел/писал) чаще всего виртуально наследуются от единственного класса на весь API, тогда меньше путаницы. Но даже этого, как оказывалось, проще избегать, чем мучаться потом.
Вот и грабли просто дают возможность получить по лбу, а уж наступает сам человек. Ясное дело, что само по себе виртуальное наследование ни в чем виноватым быть не может. Цель статьи — предостеречь от неудачного проектного решения, а не обвинить язык.
Так грабли там положили гораздо раньше, когда классов лишних придумали там, где они не нужны. Наступили, да, потом :) Если б статья называлась «Грабли 2: лишние сущности в коде», то вопросов бы не было.

Тут не то, чтобы дело в том, что надо отказываться от развесистой диаграммы классов, тк она в С++ (Java, ...) плохо ложится. А скорее то, что развесистая диаграмма классов — это слишком сложная модель в принципе, в ней много лишнего, как ее не реализуй.
Полностью согласен. Количество случаев, когда появляется желание использовать множественное наследование монотонно убывает с ростом опыта.

По сути мне в последнее время кажется, что множественное наследование не-интерфейсов полезно только при реализации низкоуровневых библиотечных механизмов, скрытых от пользователя библиотеки. Пример: наследование механизма счетчиков ссылок при (одном из вариантов) реализации умных указателей.
Но рассмотрим подробнее, откуда взялось предположение, что приведенный код должен приводить к вызову конструктора Renderable::Renderable(bool visible) вместо Renderable::Renderable().

От незнания языка С++? :)
Вы (вроде бы) забыли рассказать про одну важную проблему, связанную с виртуальным наследованием. Вариант наследования (обычное/виртуальное) выбирается при описании наследника, поэтому нельзя описать класс таким образом, чтобы гарантировать, что все его наследники унаследуются от него виртуально. Решается с помощью простой идиомы:

class ancestor_imp {
  // implementation of ancestor
};

class ancestor : virtual public ancestor_imp {
  // empty dummy class
};
Sign up to leave a comment.

Articles