In C++, le gerarchie di classi possono causare il problema del rombo (diamond problem), quando due discendenti ereditano da una classe base e poi viene creata un'altra classe che eredita da queste due. In questo caso, l'oggetto della classe discendente conterrà due copie indipendenti della classe base. Per risolvere questo problema, in C++ è stata implementata l'ereditarietà virtuale.
Se si utilizza l'ereditarietà multipla normale:
class A { public: int x; }; class B : public A {}; class C : public A {}; class D : public B, public C {};
L'oggetto D conterrà 2 copie di A: una attraverso B, l'altra — attraverso C. Questo provoca ambiguità nell'accesso a A::x e un ingiustificato consumo di memoria.
L'ereditarietà virtuale elimina la duplicazione della classe base. Una sola istanza della classe base A sarà utilizzata da tutti i discendenti:
class A { public: int x; }; class B : public virtual A {}; class C : public virtual A {}; class D : public B, public C {};
Ora D contiene solo una copia di A, e l'ambiguità nell'accesso a A::x è risolta.
Caratteristiche chiave:
Perché non si può risolvere il problema del rombo con la specifica esplicita del percorso tramite scope resolution?
La scope resolution risolve solo l'ambiguità nell'accesso ai membri della classe base, ma non rimuove le copie extra della classe base stessa. Il problema dell'inizializzazione doppia e dello stoccaggio duplicato dei dati rimane.
In quale ordine vengono chiamati i costruttori con l'ereditarietà virtuale?
I costruttori delle classi base virtuali vengono chiamati per primi e solo una volta, e direttamente dal costruttore dell'ultimo classe nella gerarchia di ereditarietà.
Esempio:
class A { public: A() { std::cout << "A "; } }; class B : public virtual A { public: B() { std::cout << "B "; } }; class C : public virtual A { public: C() { std::cout << "C "; } }; class D : public B, public C { public: D() { std::cout << "D "; } }; D d; // Output: A B C D
È obbligatorio dichiarare l'ereditarietà virtuale sia nell'intestazione che nella definizione della classe?
Sì, l'ereditarietà virtuale deve essere specificata ovunque vi sia ereditarietà dalla corrispondente classe base. Altrimenti, si verificherà necessariamente un errore di compilazione e l'ereditarietà multipla diventerà normale.
Una gerarchia complessa, in cui non è stata applicata l'ereditarietà virtuale, causando la presenza di due copie delle impostazioni nell'oggetto:
Pro:
Contro:
Utilizzata l'ereditarietà virtuale con un costruttore coerente del discendente più alto:
Pro:
Contro: