2012-06-16 12 views
11

Was passiert im folgenden Beispiel?Basisklassenreferenz - weisen Sie einen anderen Typ zu

struct B { }; 
struct D1 : B { }; 
struct D2 : B { }; 
int main() 
{ 
    D1 d; 
    D2 d2; 
    B& x = d; 
    x = d2; 
} 

Ich weiß, dass die Referenz nicht neu zugewiesen wird. x bezieht sich immer noch auf d, aber wie können Sie dann d2 zu d zuweisen?

Einige mehr:

struct B 
{ 
    B() { x = 0; } 
    int x; 
    virtual void foo() { cout << "B" << endl; } 
}; 
struct D1 : B 
{ 
    D1() { x = 1; } 
    virtual void foo() { cout << "D1" << endl; } 
}; 
struct D2 : B 
{ 
    D2() { x = 2; } 
    virtual void foo() { cout << "D2" << endl; } 
}; 

int main() 
{ 
D1 d; 
D2 d2; 
B& x = d; 
x.foo(); //D1 
       //x.x is 1 here 
x = d2; 
x.foo(); //also D1 
       //but x.x is 2 here 
} 

Es scheint, wie x.x wurde aktualisiert, aber die vftable war nicht ... Warum?

+0

+1. Gute Frage. – Nawaz

Antwort

11

x bezieht sich auf die Basisklasse BSubobjekt von d. Die Zuordnung x = d2slices das B Basissubobjekt aus d2 und ordnet ihren Wert dem Unterobjekt d zu.

Dies wird normalerweise nicht absichtlich getan.

EDIT:

Es scheint, wie x.x wurde aktualisiert, aber die vftable war nicht ... Warum?

Das ist, was der Zuweisungsoperator B::operator= tut. Basisklassen in C++ wissen überhaupt nicht, dass sie Basisklassen sind. Außerdem kann der Typ eines Objekts während seiner Lebensdauer nicht geändert werden. Die nächste Alternative ist die std::move von C++ 11, die das alte B Objekt in ein D1 Objekt in ein frisches D2 Objekt übertragen kann. Sie würden dann das alte Objekt zerstören.

+0

Hmm, kann kopieren Zuordnung virtuell gemacht werden, übrigens? (zu faul, es zu versuchen/nachzuschlagen) –

+0

Ich dachte, die Verwendung von Referenzen verhindert das Schneiden ... nein? – AMCoder

+0

Ehrlich, ich wusste das nicht. Eigentlich habe ich vorher nicht darüber nachgedacht. +1. – Nawaz

1

Wenn Sie in diesem Fall Elemente zuweisen, die nicht zur Basisklasse gehören, werden sie in Scheiben geschnitten. Das bedeutet, dass in diesem Fall kopiert wird, als ob Sie ein Basisklassenobjekt einem anderen zuweisen würden.

+0

Sprechen Sie über Objekt-Slicing (weil es so heißt)? – AMCoder

+0

Ja, ich habe diesen Begriff vergessen. – Spo1ler

3

Wenn Sie möchten, können Sie das = selbst implementieren und das Slicing "vermeiden", indem Sie nach dem entsprechenden konkreten Typ suchen (oder einen Fehler geben). Siehe folgendes Beispiel mit Fehlern.

struct B { 
    virtual B& operator = (B& b) = 0; 
}; 
struct D1 : B { 
    D1& operator = (B& b) { 
    if (dynamic_cast<D1*>(&b) == 0) { 
     cerr << "Cannot assign non D1 to D1" << endl; 
     exit(255); 
    } 
    // handle the assignments 
    return *this; 
    } 
}; 
struct D2 : B { 
    int c; 
    D2& operator = (B& b) { 
    if (dynamic_cast<D2*>(&b) == 0) { 
     cerr << "Cannot assign non D2 to D2" << endl; 
     exit(255); 
    } 
    // handle the assignments 
    return *this; 
    } 
}; 
+0

Ich denke, er fragt nach dem Verhalten, nicht, wie es geht ... –

+0

Das ist nicht, was ich gefragt habe ... – AMCoder

+0

+1, gute Idee, aber 'throw' wäre besser als' exit'. – Potatoswatter

Verwandte Themen