Eine Klasse kann von beliebig vielen Basisklassen abgeleitet werden:
class A { .... };
class B { .... };
class C { .... };
class D : public A, public B, public C
{ .... } ;
Eine Klasse darf nicht mehr als einmal als direkte Basisklasse auftreten:
class D : public A, public A // FALSCH!
{ .... };
Grund: Jeder Zugriff auf die Elemente von A wäre mehrdeutig:
class A : { public: int val; };
class D : public A, public A // FALSCH!
{ .... };
void f (D* pd)
{ pd->val = 42; // mehrdeutig !!!
}
Indirekte Basisklassen können jedoch mehrfach auftreten:
class A { .... };
class B : public A { .... };
class C : public A { .... };
class D : public B, public C // OK
{ .... }
Das Diagramm der Klassenhierarchie sieht wie folgt aus:
Ein Objekt der Klasse D könnte folgendes Speicherlayout haben:
Mehrdeutigkeiten können durch Qualifizierung mit dem :: Operator aufgelöst werden:
class A { public int val; };
class B : public A { .... };
class C : public A { .... };
class D : public B, public C
{ .... }
int D::f() { return C::val + B::val;}
Mehrdeutige Namen führen nicht zu Fehlern, solange man sie nicht benutzt!
In vielen Fällen ist die Mehrfachvererbung der selben Basisklasse nicht nötig und/oder erwünscht:
amphibienfahrzeug sollte nur eine Komponente der Basisklasse fahrzeug enthalten:
Man erreicht dies, in dem man die entsprechende Basisklasse virtuell vererbt:
class fahrzeug { .... };
class landfahrzeug
: public virtual fahrzeug
{ ... };
class wasserfahrzeug
: public virtual fahrzeug
{ ... };
class ampibienfahrzeug
: public landfahrzeug,
public wasserfahrzeug
{ .... };
Speicherlayout für ein Objekt der Klasse amphibienfahrzeug:
Mehrdeutigkeiten können so nicht auftreten.
Besonderheiten
In unserem Beispiel wird fahrzeug von amphibienfahrzeug konstruiert. Geschieht dies nicht explizit, wird der Default-Konstruktor aufgerufen (deshalb muß es ihn geben).
Beispiel:
class fahrzeug
{ public: fahrzeug()
{cout << "\tdefault-fahrzeug\n"; }
};
class landfahrzeug: public virtual fahrzeug
{ public: landfahrzeug()
{cout << "\tdefault-landfahrzeug\n"; }
};
class wasserfahrzeug: public virtual fahrzeug
{ public: wasserfahrzeug()
{cout << "\tdefault-wasserfahrzeug\n"; }
};
class amphibienfahrzeug: public landfahrzeug,
public wasserfahrzeug
{ public: amphibienfahrzeug()
{cout << "\tdefault-amphibienfahrzeug\n"; }
};
int main()
{ cout << "Ein fahrzeug:\n";
fahrzeug f;
cout << "Ein landfahrzeug:\n";
landfahrzeug lf;
cout << "Ein amphibienfahrzeug:\n";
amphibienfahrzeug af;
}
schreibt:
Ein fahrzeug:
default-fahrzeug
Ein landfahrzeug:
default-fahrzeug
default-landfahrzeug
Ein amphibienfahrzeug:
default-fahrzeug
default-landfahrzeug
default-wasserfahrzeug
default-amphibienfahrzeug
Mehrdeutigkeiten bei Verwendung virtueller Klassen können durch die Dominator-Regel aufgelöst werden: Ein Name B::x dominiert einen Namen A::x wenn A eine Basisklasse von B ist.
Bei Mehrdeutigkeiten zwischen Namen, wird der genommen, der die anderen dominiert:
D::g()
{ x++; // das ist B::x
}
Mehrdeutigkeiten sind natürlich auch mit Dominator-Regel noch möglich:
class A
{ public : char x;
};
class B: public virtual A
{ public: char x;
char y;
};
class C: public virtual A
{ public: char y;
};
class D: public B, public C
{ public: char z;
D() { z=x+y;}
};
Compiler:
error: ambiguous B::y and C::y
Compilation failed
Bei Mehrfachvererbung kann Typumwandlung den Wert eines Pointers verändern:
class A {...};
class B {...};
class C: public A, public B
{...}
C* pc = new C;
B *pb;
Umwandlung von pc in einen B*
pb = pc;
oder
pb = (B*) pc;
setzt pb auf den B-Anteil von pc.
Experiment
#include <iostream.h>
class A { int a;};
class B { int b;};
class C: public A, public B
{int c;};
int main()
{ C* pc = new C;
B *pb;
pb = pc;
cout << "pc: " << (long)pc << endl;
cout << "pb: " << (long)pb << endl;
if (pc == pb)
cout << "pb und pc sind gleich\n";
}
erzeugt
pc: 142424
pb: 142428
pb und pc sind gleich
Der Vergleich von Zeigern auf Elemente von Klassenhierarchien berücksichtigt also die vorgenommenen Typumwandlungen.