In C++ kunnen klassehiërarchieën het diamantprobleem (diamond problem) veroorzaken, wanneer twee afgeleiden klassen van één basis klasse erven, en vervolgens een andere klasse wordt gemaakt die van deze twee erft. In dat geval bevat een object van de afgeleide klasse twee onafhankelijke kopieën van de basis klasse. Om dit probleem op te lossen, is virtuele overerving in C++ geïmplementeerd.
Als je gewone meerdere overerving gebruikt:
class A { public: int x; }; class B : public A {}; class C : public A {}; class D : public B, public C {};
Bevat object D 2 kopieën van A: één via B, de andere via C. Dit veroorzaakt ambiguïteit bij toegang tot A::x en een onterecht geheugenverbruik.
Virtuele overerving elimineert de duplicatie van de basis klasse. Eén instantie van de basis klasse A zal door alle afstammelingen worden gebruikt:
class A { public: int x; }; class B : public virtual A {}; class C : public virtual A {}; class D : public B, public C {};
Nu bevat D slechts één kopie van A, en de ambiguïteit met toegang tot A::x wordt opgelost.
Belangrijkste kenmerken:
Waarom kan het diamantprobleem niet worden opgelost door expliciet het pad via scope resolution aan te geven?
Scope resolution lost alleen de ambiguïteit op bij toegang tot leden van de basis klasse, maar elimineert niet de overbodige kopieën van de basis klasse zelf. Het probleem van dubbele initiatie en dubbele opslag van gegevens blijft bestaan.
In welke volgorde worden constructors aangeroepen bij virtuele overerving?
Constructors van virtuele basis klassen worden als eerste aangeroepen en slechts één keer, door de constructor van de laatste klasse in de overervingshiërarchie.
Voorbeeld:
class A { public: A() { std::cout << "A\n"; } }; class B : public virtual A { public: B() { std::cout << "B\n"; } }; class C : public virtual A { public: C() { std::cout << "C\n"; } }; class D : public B, public C { public: D() { std::cout << "D\n"; } }; D d; // Output: A B C D
Is het verplicht om virtuele overerving zowel bij de declaratie als bij de definitie van de klasse aan te geven?
Ja, virtuele overerving moet overal worden aangegeven waar sprake is van overerving van de betreffende basis klasse. Een compilatiefout is anders onvermijdelijk, en meerdere overerving zal gewoon zijn.
Een complexe hiërarchie waarin geen virtuele overerving is toegepast, waardoor er twee kopieën van de instellingen in het object zijn:
Voordelen:
Nadelen:
Gebruik van virtuele overerving met een consistente constructor van de hoogste afstammeling:
Voordelen:
Nadelen: