Classi base virtuali
Poiché una classe può rappresentare più di una volta una classe base indiretta per una classe derivata, in C++ è possibile ottimizzare l'utilizzo di tali classi base. Le classi base virtuali consentono di risparmiare spazio e di evitare ambiguità in gerarchie di classi in cui viene utilizzata l'ereditarietà multipla.
Ogni oggetto non virtuale contiene una copia dei membri dati definiti nella classe base. Questa duplicazione determina uno spreco di spazio e richiede di specificare la copia dei membri della classe base desiderata ogni volta che vi si accede.
Quando una classe base viene specificata come base virtuale, può essere utilizzata più volte come base indiretta senza duplicazione dei membri dati. Una sola copia dei membri dati viene condivisa da tutte le classi base che la utilizzano come base virtuale.
Nella dichiarazione di una classe base virtuale la parola chiave virtual è presente negli elenchi di base delle classi derivate.
Si consideri la gerarchia di classi nella figura seguente, che illustra un oggetto Lunch-Line simulato.
Rappresentazione grafica di un oggetto Lunch-line simulato
Nella figura Queue è la classe base sia per CashierQueue che per LunchQueue. Tuttavia, quando entrambe le classi vengono combinate per formare LunchCashierQueue, si verifica il problema seguente: la nuova classe contiene due oggetti secondari di tipo Queue, uno da CashierQueue e l'altro da LunchQueue. Nella figura seguente viene illustrato il layout di memoria concettuale (il layout di memoria effettivo può essere ottimizzato).
Oggetto Lunch-line simulato
Si noti che sono presenti due oggetti secondari Queue nell'oggetto LunchCashierQueue. Nel codice seguente Queue viene dichiarato come una classe base virtuale:
// deriv_VirtualBaseClasses.cpp
// compile with: /LD
class Queue {};
class CashierQueue : virtual public Queue {};
class LunchQueue : virtual public Queue {};
class LunchCashierQueue : public LunchQueue, public CashierQueue {};
La parola chiave virtual garantisce che sia inclusa solo una copia dell'oggetto subordinato Queue (vedere la figura seguente).
Oggetto Lunch-line simulato con classi di base virtuali
Alla classe possono essere associati sia un componente virtuale che uno non virtuale di un tipo specifico. Ciò si verifica in condizioni illustrate nella figura seguente.
Componenti virtuali e non virtuali della stessa classe
Nella figura CashierQueue e LunchQueue utilizzano Queue come classe base virtuale. Tuttavia, TakeoutQueue specifica Queue come classe base, non come classe base virtuale. Di conseguenza, LunchTakeoutCashierQueue dispone di due oggetti secondari di tipo Queue: uno dal percorso di ereditarietà che include LunchCashierQueue e uno dal percorso che include TakeoutQueue. Questa situazione viene illustrata nella figura seguente.
Layout dell'oggetto con ereditarietà virtuale e non virtuale
Nota
L'ereditarietà virtuale offre vantaggi significativi in termini di dimensione se paragonata all'ereditarietà non virtuale.Può introdurre tuttavia un ulteriore sovraccarico di elaborazione.
Se una classe derivata esegue l'override di una funzione virtuale che eredita da una classe base virtuale e se un costruttore o un distruttore per la classe base derivata chiama tale funzione utilizzando un puntatore alla classe base virtuale, il compilatore può introdurre campi vtordisp nelle classi con basi virtuali. L'opzione del compilatore /vd0 elimina l'aggiunta del membro di spostamento nascosto del costruttore o distruttore. L'opzione del compilatore /vd1 li abilita per impostazione predefinita dove sono necessari. Disattivare vtordisps solo se si è certi che tutti i costruttori e distruttori di classe chiamino virtualmente le funzioni virtuali.
L'opzione del compilatore /vd influisce su un intero modulo di compilazione. Utilizzare il pragma vtordisp per eliminare e quindi riabilitare i campi vtordisp classe per classe:
#pragma vtordisp( off )
class GetReal : virtual public { ... };
#pragma vtordisp( on )