Ereditarieta` multipla
Implicitamente e` stato supposto che una classe potesse essere derivata solo
da una classe base, in effetti questo e` vero per molti linguaggi, tuttavia
il C++ consente l'ereditarieta` multipla. In questo modo e` possibile far ereditare
ad una classe le caratteristiche di piu` classi basi, un esempio e` dato dall'implementazione
della libreria per l'input/output di cui si riporta il grafo della gerarchia
(in alto le classi basi, in basso quelle derivate):
come si puo` vedere esistono diverse classi ottenute per ereditarieta` multipla,
iostream ad esempio che ha come classi basi istream
e ostream .
La sintassi per l'ereditarieta` multipla non si discosta da quella per l'ereditarieta`
singola, l'unica differenza e` che bisogna elencare tutte le classi basi separandole
con virgole; al solito se non specificato diversamente per default l'ereditarieta`
e` privata. Ecco un esempio tratto dal grafo precedente:
class iostream : public istream, public ostream {
/* ... */
};
L'ereditarieta` multipla comporta alcune problematiche che non si presentano
in caso di ereditarieta` singola, quella a cui si puo` pensare per prima e`
il caso in cui le stesse definizioni siano presenti in piu` classi base (name
clash):
class BaseClass1 {
public:
void Foo();
void Foo2();
/* ... */
};
class BaseClass2 {
public:
void Foo();
/* ... */
};
class Derived : BaseClass1, BaseClass2 {
// Non ridefinisce Foo()
/* ... */
};
La classe Derived eredita piu` volte gli stessi membri e in particolare
la funzione Foo() , quindi una situazione del tipo
Derived A;
/* ... */
A.Foo() // Errore, e` ambiguo!
non puo` che generare un errore perche` il compilatore non sa a quale membro
si riferisce l'assegnamento. Si noti che l'errore viene segnalato al momento
in cui si tenta di chiamare il metodo e non al momento in cui Derived
eredita, il fatto che un membro sia ereditato piu` volte non costituisce di
per se alcun errore.
Rimane comunque il problema di eliminare l'ambiguita` nella chiamata di Foo() ,
la soluzione consiste nell'utilizzare il risolutore di scope indicando esplicitamente
quale delle due Foo() :
Derived A;
/* ... */
A.BaseClass1::Foo() // Ok!
in questo modo non esiste piu` alcuna ambiguita`.
Alcune osservazioni:
- quanto detto vale anche per i membri dato;
- non e` necessario che la stessa definizione si trovi in piu` classi basi
dirette, e` sufficiente che essa giunga alla classe derivata attraverso due
classi basi distinte, ad esempio (con riferimento alla precedenti dichiarazioni):
class FirstDerived : public BaseClass2 {
/* ... */
};
class SecondDerived : public BaseClass1,
public FirstDerived {
/* ... */
};
Nuovamente SecondDerived presenta lo stesso problema, e` cioe`
sufficiente che la stessa definizione giunga attraverso classi basi
indirette (nel precedente esempio BaseClass2 e` una classe
base indiretta di SecondDerived );
- il problema non si sarebbe posto se
Derived avesse ridefinito
la funzione membro Foo() .
Il problema diviene piu` grave quando una o piu` copie della stessa definizione
sono nascoste dalla keyword private nelle classi basi (dirette
o indirette), in tal caso la classe derivata non ha alcun controllo su quella
o quelle copie (in quanto vi accede indirettamente tramite le funzioni membro
ereditate) e il pericolo di inconsistenza dei dati diviene piu` grave.
|