In C ++ una classe può ereditare (direttamente o indirettamente) da più di una classe, che viene definita
ereditarietà multipla .
C # e Java, tuttavia, limitano le classi a una singola ereditarietà, ciascuna classe eredita da una singola classe padre.
L'ereditarietà multipla è un modo utile per creare classi che combinano aspetti di due gerarchie di classi disparate, cosa che spesso accade quando si utilizzano framework di classi differenti all'interno di una singola applicazione.
Se due framework definiscono le proprie classi di base per le eccezioni, ad esempio, è possibile utilizzare più ereditarietà per creare classi di eccezioni che possono essere utilizzate con entrambi i framework.
Il problema con l'ereditarietà multipla è che può portare a ambiguità. L'esempio classico è quando una classe eredita da altre due classi, ognuna delle quali eredita dalla stessa classe:
class A {
protected:
bool flag;
};
class B : public A {};
class C : public A {};
class D : public B, public C {
public:
void setFlag( bool nflag ){
flag = nflag; // ambiguous
}
};
In questo esempio, il flag
membro dati è definito da class A
. Ma class D
discende da class B
e class C
, da cui derivano entrambi A
, quindi in sostanza sono disponibili due copie di flag
perché due istanze di si A
trovano nella D
gerarchia di classi di. Quale vuoi impostare? Il compilatore si lamenterà del fatto che il riferimento a flag
in D
è ambiguo . Una soluzione consiste nel disambiguare esplicitamente il riferimento:
B::flag = nflag;
Un'altra soluzione è dichiarare B e C come virtual base classes
, il che significa che solo una copia di A può esistere nella gerarchia, eliminando ogni ambiguità.
Esistono altre complessità con l'ereditarietà multipla, come l'ordine in cui le classi base vengono inizializzate quando viene costruito un oggetto derivato o il modo in cui i membri possono essere nascosti inavvertitamente dalle classi derivate. Per evitare queste complessità, alcuni linguaggi si limitano al più semplice modello di ereditarietà singola.
Sebbene ciò semplifichi notevolmente l'ereditarietà, ne limita anche l'utilità perché solo le classi con un antenato comune possono condividere comportamenti. Le interfacce mitigano in qualche modo questa restrizione consentendo alle classi in gerarchie diverse di esporre interfacce comuni anche se non sono implementate condividendo codice.