Meervoudige overerving stelt een klasse in staat om de interface en implementatie van meer dan één basisklasse te erven. Dit is een krachtige functie van C++ die sinds de vroege dagen van de taal breed werd gebruikt voor het implementeren van complexe architecturen.
Geschiedenis van de kwestie: Meervoudige overerving werd aan C++ toegevoegd als een middel om herbruikbare componenten te creëren en verschillende rollen in één klasse te combineren (bijvoorbeeld wanneer een object tegelijkertijd een thread en een queue is).
Probleem: De belangrijkste complicaties bij meervoudige overerving zijn het 'diamond problem', ambiguïteit bij het aanspreken van leden van basisklassen en de onduidelijkheid over de volgorde van aanroepingen van constructors/destructors.
Oplossing: Om de bovengenoemde problemen te vermijden, heeft C++ virtuele overerving geïntroduceerd. Dit garandeert dat een gemeenschappelijke voorouder precies één keer wordt gemaakt, zelfs als er meerdere ketens naar hem toe leiden in de hiërarchie, en zorgt voor de juiste volgorde van initialisatie/destructie.
Voorbeeldcode:
class A { public: int value; A() : value(1) {} }; class B : virtual public A {}; class C : virtual public A {}; class D : public B, public C {}; int main() { D d; d.value = 10; // OK, slechts één A return 0; }
Belangrijke kenmerken:
Als een basisklasse twee keer is geërfd (links en rechts), hoeveel kopieën van deze klasse zullen er dan in het geheugen van het object zijn?
Standaard twee, tenzij virtuele overerving wordt gebruikt. Alleen bij virtuele overerving zal er precies één kopie zijn.
Kun je expliciet aangeven tot welke basisklasse het lid behoort als er ambiguïteit optreedt?
Ja, door kwalificaties te gebruiken:
d.B::value = 5; d.C::value = 6;
Hoe wordt de volgorde van aanroepingen van constructors en destructors bepaald in het geval van meervoudige overerving?
De volgorde van aanroepingen van constructors volgt de volgorde van declaratie van basisklassen in de overervingslijst (van links naar rechts), en vervolgens de afgeleide klasse zelf. Voor destructors is het omgekeerd.
Een programmeur implementeert een logging- en queue-systeem via meervoudige overerving, zich niet bewust van het diamond problem. Dit resulteert in een dubbele initialisatie van de gemeenschappelijke logger, wat leidt tot conflicten bij het vrijgeven van middelen.
Voordelen:
Nadelen:
Virtuele overerving wordt gebruikt voor de gemeenschappelijke logger, en de leden van de klasse worden expliciet geïnitialiseerd in de constructor.
Voordelen:
Nadelen: