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.