Frage

#include <iostream>
using namespace std;

class A             { public: void eat(){ cout<<"A";} };
class B: public A   { public: void eat(){ cout<<"B";} };
class C: public A   { public: void eat(){ cout<<"C";} };
class D: public B,C { public: void eat(){ cout<<"D";} };

int main(){
    A *a = new D();
    a->eat();
}

Ich bin nicht sicher, dass diese Diamanten Problem genannt oder nicht, aber warum funktioniert das nicht?

Ich habe die defination für eat() für D gegeben. Von daher ist es auch nicht B oder die C Kopie verwenden muß (so sollte es kein Problem).

Als ich sagte, a->eat() (erinnern eat() nicht virtuell ist), gibt es nur eine mögliche eat() zu Anruf ist, dass der A.

Warum dann bekomme ich diesen Fehler:

  

'A' ist eine mehrdeutige Basis von 'D'


Was genau bedeutet A *a = new D(); Mittelwert an den Compiler ??

und

Warum das gleiche Problem nicht auf, wenn ich D *d = new D(); verwenden?

War es hilfreich?

Lösung

Stellen Sie sich ein etwas anderes Szenario

class A             { protected: int a; public: void eat(){ a++; cout<<a;} };
class B: public A   { public: void eat(){ cout<<a;} };
class C: public A   { public: void eat(){ cout<<a;} };
class D: public B,C { public: void eat(){ cout<<"D";} };

int main(){
    A *a = new D();
    a->eat();
}

Wenn das funktionieren würde, würde es die a in B oder die a in C erhöhen? Deshalb ist es nicht eindeutig. Der this Zeiger und jedes nicht-statisches Datenelement unterscheidet sich für die beiden A Subobjekte (von denen einer durch den B Subobjekt enthalten ist, und die andere von der C Subobjekt). Versuchen Sie, Ihren Code wie folgt zu ändern und es wird funktionieren (dass es kompiliert und druckt „A“)

class A             { public: void eat(){ cout<<"A";} };
class B: public A   { public: void eat(){ cout<<"B";} };
class C: public A   { public: void eat(){ cout<<"C";} };
class D: public B, public C { public: void eat(){ cout<<"D";} };

int main(){
    A *a = static_cast<B*>(new D());
      // A *a = static_cast<C*>(new D());
    a->eat();
}

Das wird jeweils eat auf dem A subobject von B und C nennen.

Andere Tipps

Die Diamant Ergebnisse in zwei Instanzen von A in der D-Objekt, und es ist nicht eindeutig, welche Sie beziehen sich auf - müssen Sie virtuelle Vererbung verwenden, um diese zu lösen:

class B: virtual public A   { public: void eat(){ cout<<"B";} };
class C: virtual public A   { public: void eat(){ cout<<"C";} };

unter der Annahme, dass Sie eigentlich nur eine Instanz wollen. Ich nehme an, Sie auch wirklich meinte:

class D: public B, public C { public: void eat(){ cout<<"D";} };

Beachten Sie, dass der Compiler-Fehler ist auf dem "A * a = new D ();" Linie, nicht auf den Anruf zu „essen“.

Das Problem ist, dass, weil Sie nicht-virtuelle Vererbung verwendet, um Sie mit der Klasse A zweimal am Ende: einmal durch B und einmal durch C. Wenn Sie zum Beispiel eines Mitglied m bis A hinzufügen, dann D zwei von ihnen haben: B :: m und C :: m.

Manchmal wollen Sie wirklich A zweimal in der Ableitung Graph haben, in dem Fall, dass Sie immer angeben müssen, welche A Sie sprechen. In D, würden Sie in der Lage sein, um Referenz B :: m und C :: m getrennt.

Manchmal, wenn Sie wirklich wollen, nur ein A, in diesem Fall müssen Sie zu verwenden virtuelle Vererbung .

Für eine wirklich ungewöhnliche Situation, Antwort Neil ist eigentlich falsch (zumindest teilweise).

Mit aus virtueller Vererbung, erhalten Sie zwei getrennte Kopien von A in der letzten Aufgabe.

„Der Diamant“ Ergebnisse in einer einzigen Kopie der A im endgültigen Objekt und erzeugt durch mit virtueller Vererbung:

alt text

Da die „Diamant“ bedeutet nur, es gibt eine Kopieren von A im endgültigen Objekt erzeugt ein Verweis auf A keine Zweideutigkeit. Ohne virtuelle Vererbung, ein Verweis auf A zu einem von zwei verschiedenen Objekten (die auf der linken Seite oder auf der rechten Seite im Diagramm) beziehen könnte.

Der Fehler Sie bekommen kommt nicht von eat() Aufruf - es ist von der Linie kommt vor. Es ist die upcast selbst, das die Mehrdeutigkeit erzeugt. Als Neil Butter weist darauf hin, gibt es zwei Kopien von A in Ihrem D, und der Compiler nicht weiß, welche Sie a Punkt wollen bei.

Sie mögen: (Erreichbare mit virtueller Vererbung)

  

D
  / \
  B C
  \ /
  A

Und nicht: (Was ohne virtuelle Vererbung geschieht)

  

D
  / \
  B C
  | |
  A A

Virtuelle Vererbung bedeutet, dass es nur 1 Instanz der Basis A Klasse nicht 2.

sein

Ihre Art D würde 2 VTable-Zeiger haben (Sie können sie im ersten Diagramm sehen), eine für B und eine für C, die praktisch erben A. D Objektgröße erhöht, da es 2 Zeiger speichert jetzt; jedoch gibt es nur eine A jetzt.

So B::A und C::A gleich sind und so kann es keine zweideutigen Anrufe von D sein. Wenn Sie nicht virtuelle Vererbung verwenden, müssen Sie das zweite Diagramm oben. Und jeder Anruf an ein Mitglied der A dann mehrdeutig wird, und Sie müssen angeben, welchen Weg Sie nehmen wollen.

Wikipedia hat einen weiteren guten Überblick und Beispiel hier

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top