Виртуално наследяване на Rake 2
Статия за това как множественото наследяване усложнява нещата. Как виртуалното наследство, на пръв поглед, се прилага нелогично. На втори поглед изглежда логиката, но нивото на сложност и сложност продължава да расте. Като цяло, колкото по-трудна е задачата, толкова по-прости трябва да бъдат инструментите.
Всичко е базирано на реални събития, но примерите са максимално опростени, така че в тях да остане само същността на проблема. И така, в разработеното приложение бяха използвани голям брой еднакво обработени обекти. Освен това някои от тях бяха показани, други трябваше да се актуализират постоянно, а трети комбинираха и двете. Съответно имаше желание да се внедрят три базови класа
- Renderable: съдържа флаг за видимост и метод за рисуване
- Актуализирано: съдържа флаг за активност и метод за актуализиране на състоянието
- VisualActivity= Възможност за изобразяване + Възможност за актуализиране
- JustVisible: само видим обект
- JustVisiblePlusVisualActivity: JustVisible със състояние, което може да се актуализира

Проблемът се вижда веднага - крайният клас наследява Renderable два пъти: като родител на JustVisible и VisualActivity. Това не работи правилно със списъци с показвани обекти.
Получават се двусмислени конверсии - компилаторът не може да разбере койRenderableе наследил от кой клон става дума. Може да се помогне чрез изясняване на посоката чрез изрично кастинг към един от междинните
Компилацията ще успее, но грешката остава. В нашия случай имахме нужда от същотоВъзможност за изобразяване, независимо как е наследена. Факт е, че в случай на нормално наследяване класът потомък (JustVisiblePlusVisualActivity) съдържа отделен екземпляр на родителския клас за всеки клон.

Освен това свойствата на всеки от тях могат да се променят независимо. Изразен в c++, изразът е верен
Така че обичайното множествено наследяване не беше подходящо за задачата. Но виртуалното изглеждаше като сребърния куршум, който беше необходим ... Всичко, което се изискваше, беше виртуално да се наследят базовите класовеRenderableиUpdatable, а останалите - по обичайния начин:
Всички виртуално наследени класове са представени само веднъж в наследника. И всичко щеше да работи, ако базовите класове нямаха конструктори с параметри. Но такива дизайнери съществуваха и имаше изненада. Всеки виртуално наследен клас имаше както конструктор по подразбиране, така и параметризиран.
Потомствените класове съдържаха само конструктори с параметри
И все пак при създаване на обект
конструкторът по подразбиранеRenderableбеше извикан! На пръв поглед изглеждаше като нещо диво. Но нека да разгледаме по-отблизо откъде идва предположението, че горният код трябва да води до извикване на конструктораRenderable::Renderable(bool visible)вместоRenderable::Renderable().

Проблемът е в предположението, чеRenderableпо чудо ще се раздели междуJustVisible,VisualActivityиJustVisiblePlusUpdate. Но "чудото" не беше предопределено да се случи. В крайна сметка тогава бихте могли да напишете нещо подобно
предоставяне на противоречива информация на компилатора, когато това би изисквало конструиране наRenderableс параметриtrueиfalseедновременно. Отворетеникой не искаше възможността за подобни парадокси и съответно механизмът работи по друг начин. КласътRenderableв нашия случай вече не е част нито отJustVisible, нито отVisualActivity, а принадлежи директно къмJustVisiblePlusUpdate.

Това обяснява защо конструкторът по подразбиране беше извикан - конструкторите на виртуални класове трябва да бъдат извикани от крайните потомци, т.е. заобиколно решение би било нещо подобно
При виртуалното наследяване, в допълнение към конструкторите на непосредствените родители, е необходимо изрично да се извикат конструкторите на всички виртуално наследени класове. Това не е много очевидно и лесно може да бъде пренебрегнато в нетривиален проект. Така още веднъж истината беше потвърдена:няма повече от едно отворено наследяване за всеки клас. Не си струва. В нашия случай беше решено да изоставим разделението наRenderableиUpdatable, ограничавайки се до една базаVisualActivity. Това добави известна излишност, но драстично опрости цялостната архитектура - беше твърде скъпо да се проследяват и поддържат всички случаи на виртуално и обикновено наследяване.
И тук можете да получите грант за тестов период на Yandex.Cloud. Необходимо е само да въведете "Habr" в полето "секретна парола".