Виртуално наследство - Проблем с диаманти - Какво наистина се случва

Разбирам и съм чел достатъчно за проблема с диамантите, който се решава чрез виртуално наследяване. Въпросът ми тук е

„Какво всъщност означава поставянето на виртуален до базов клас, от който бихте наследили“

class A { public: void Foo() {} };
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};

Исках да знам дали моето разбиране е правилно. В изявлението

  class D : public B, public C {}

Компилаторът ще премине през първия базов клас B и ще забележи, че той практически наследява от class A. Компилаторът ще провери дали екземплярът на клас A присъства, ако не, той ще създаде екземпляр на A, който е извлечен от B. След това компилаторът ще премине през клас C и ще забележи, че той наследява виртуално от клас A. Въпреки това, тъй като е наследен виртуално от C и екземпляр на A вече присъства, той няма да включва нов екземпляр. По този начин проблемът с диаманта се решава. Правилно ли е това разбиране?


person Rajeshwar    schedule 02.04.2014    source източник
comment
Да, вашето разбиране е правилно. За отлично обяснение по тази тема можете да прочетете книгата Inside Object C++ Model от Lippman.   -  person Mantosh Kumar    schedule 02.04.2014


Отговори (1)


Да, вашето разбиране е правилно, но би било трудно да се кажат сценарии, при които виртуалното наследяване е наистина разумно и дори в тези случаи проблемът обикновено може да бъде решен с нещо друго, което е по-просто.

Във вашия случай вашият D екземпляр ще бъде композиция от C, B и единичен A екземпляр. Тъй като и A, и B могат да участват в различни сценарии с различни композиции, където A се наследява практически от много класове (може би дори повече класове от A и B), не е ясно как да се оформят например градивните елементи на D инстанция в памет. Поради тази причина кодът, компилиран за класове B и C, обикновено използва помощни указатели или отмествания (1 указател на екземпляр, един за B и един за C), които сочат към екземпляра A, който се споделя от кода на различни класове, които наследяват на практика от клас A. Това заема допълнително място във вашия D екземпляр и прави вашия код по-малко ефективен, като си играете с тези указатели.

Друга грозна подробност на изпълнението е, че в случай на вашия D клас, когато създавате/инициализирате екземпляр на D, вашите B и C екземпляри се „състезават“ за извикване на конструктора на клас A от техните конструктори. Поради тази причина вашият екземпляр D също ще съдържа променлива bool, която показва дали някой вече е извикал ctor на A от неговия конструктор. Един от ctors на B и C ще спечели и ще зададе тази bool променлива като вярна, така че следващите опити, идващи от ctors на други "виртуални потомци", просто се спасяват след проверка на тази конкретна bool стойност.

Виртуалното наследяване е просто грозно и избягвайте, когато е възможно.

person pasztorpisti    schedule 02.04.2014
comment
Няма включено състезание -- конструкторите винаги се извикват стриктно в ред отдолу нагоре и отляво надясно (така че конструирането на D винаги ще включва първо конструиране на A, след това B, след това C). Също така, разумните реализации ще съхраняват базовото отместване A във vtable и няма да използват допълнителна памет за екземпляр. - person Chris Dodd; 03.04.2014
comment
@ChrisDodd това има смисъл, беше доста отдавна, когато проверих тази реализация и тя всъщност съхрани указателя в екземпляра. Все пак дори внедряването на vtable има своите недостатъци, повече търсения в замяна на по-ефективно използване на паметта, но все още не прави виртуалното наследяване привлекателно. Относно състезанията имах предвид, че в изпълнението, което проверих, всички извлечени ctorи всъщност са извикали ctor на A, но само първото извикване е постигнало целта си. Вие сте прав, че състезание може да не е точната дума тук, тъй като звучи така, сякаш имаме проблем, свързан с многопоточност тук. :-) - person pasztorpisti; 03.04.2014
comment
По отношение на трудни за разбиране сценарии, при които виртуалното наследяване е наистина разумно: погледнете този отговор и това следва- въпрос, който публикувах. - person Oguk; 20.06.2014