next up previous contents index
Next: 50 Ways to Improve Up: Klassen-Hierarchien und Vererbung Previous: Virtuelle Funktionen

Mehrfache Vererbung

  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:

tex2html_wrap5261

Ein Objekt der Klasse D könnte folgendes Speicherlayout haben:

tex2html_wrap5263

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!  

Virtuelle Basisklassen

    In vielen Fällen ist die Mehrfachvererbung der selben Basisklasse nicht nötig und/oder erwünscht:

tex2html_wrap5265

amphibienfahrzeug sollte nur eine Komponente der Basisklasse fahrzeug enthalten:

tex2html_wrap5267

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:

tex2html_wrap5269

Mehrdeutigkeiten können so nicht auftreten.  

Besonderheiten

tex2html_wrap5267

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:

tex2html_wrap5273

    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

Mehrfache Vererbung und Typumwandlung

    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.


next up previous contents index
Next: 50 Ways to Improve Up: Klassen-Hierarchien und Vererbung Previous: Virtuelle Funktionen

Peter Pfahler, 1997