2016-06-16 13 views
0

ich vor kurzem ein Problem mit Abgüssen und Mehrfachvererbung hatte: Ich habe eine Base*-Unrelated*, werfen benötigt, da eine bestimmte Derived Klasse die Unrelated-Klasse abgeleitet.Mehrfachvererbung Besetzung funktioniert nicht wie erwartet

Dies ist ein kurzes Beispiel:

#include <iostream> 

struct Base{ 
    virtual ~Base() = default; 
}; 

struct Unrelated{ 
    float test = 111;  
}; 

struct Derived : Base,Unrelated{}; 

int main(){ 
    Base* b = new Derived; 
    Unrelated* u1 = (Unrelated*)b; 
    std::cout << u1->test << std::endl; //outputs garbage 
    Unrelated* y = dynamic_cast<Unrelated*>(b); 
    std::cout << y->test << std::endl; //outputs 111 
} 

Die erste Besetzung eindeutig nicht funktioniert, aber die zweite hat funktioniert. Meine Frage ist: Warum hat die zweite Besetzung funktioniert? Sollte dynamic_cast nur für Cast zu einem verwandten Klassentyp funktionieren? Ich dachte, es gab keine Informationen über Unrelated zur Laufzeit, weil es nicht polymorph ist.

Edit: Ich habe culirus gcc für das Beispiel verwendet.

+1

Sie behalten den Zeiger auf abgeleitete Instanz in b und Sie werfen es auf Unrelated, von dem Sie erben. Was ist das Problem? – threaz

+0

'Unrelated' ist eine Basis von' Derived' und Sie transformieren von 'Derived *' nach 'Unrelated *', weshalb dies gültig ist. – ArchbishopOfBanterbury

+2

Eek. C-Stil wirft. Benutze sie nicht. Verwenden Sie stattdessen den richtigen C++ - Cast - immer. –

Antwort

8

dynamic_cast funktioniert, weil der dynamische Typ von Objekt durch einen Zeiger auf seine Basisklasse gerichtet ist auf Unrelated verwendet.

Beachten Sie, dass dynamic_cast eine virtuelle Tabelle benötigt, um den Vererbungsbaum des Objekts zur Laufzeit zu überprüfen.

Der C-Style Cast (Unrelated*)b funktioniert nicht, weil the C-style cast does const_cast, static_cast, reinterpret_cast and more, but it does not do dynamic_cast.

Ich würde vorschlagen, C-Style-Umwandlungen in C++ - Code zu vermeiden, weil sie so viele Dinge im Gegensatz zu präzisen C++ - Umwandlungen tun. Meine Kollegen, die darauf bestehen, C-Style-Cast zu verwenden, bringen sie immer noch gelegentlich in die Irre.

4

Der erste Guss (Unrelated*)b funktioniert nicht, weil Sie das Base Klasse Sub-Objekt sind die Behandlung, wahrscheinlich nur einen VTable-Zeiger enthalten, als Unrelated, ein float enthalten.

Stattdessen können Sie nach unten und oben, static_cast<Unrelated*>(static_cast<Derived*>(b)) werfen.

Und das ist, was dynamic_cast für Sie tut, da Base ein polymorpher Typ ist (mindestens eine virtuelle Methode), die dynamic_cast den Typ des am weitesten abgeleitete Objekt inspizieren können.

Im Vorübergehen würde dynamic_cast<void*>(b) Ihnen einen Zeiger auf das am meisten abgeleitete Objekt geben.

Da Sie jedoch die Typen kennen, ist es nicht nötig, den geringen Aufwand einer dynamic_cast aufzurufen: tun Sie einfach die Down- und Up-Casts.


Anstelle des C Stil Guss(Unrelated*)b Sie das entsprechende C++ namens oder Abgüsse Guss verwenden sollten, weil C-Stil Abgüsse von Zeigern können Dinge tun würde man nicht erwarten, und weil die Wirkung kann sich komplett ändern, wenn während der Wartung die Typen gewechselt werden.

Der C-Style-Cast wird maximal 2 C++ genannte Casts ausführen. In diesem Fall entspricht der C-Style-Cast einem reinterpret_cast. Der Compiler erlaubt hier keine andere benannte Besetzung.

Welches ist ein Warnzeichen.;-)

Im Gegensatz dazu sind die Down- und Up-Casts static_cast s, die normalerweise gutartige Casts sind.


Alles, was gesagt, ist der beste fast vollständig Abgüsse zu vermeiden, indem die streng geheime Technik:

nicht Typinformationen an erster Stelle wegzuwerfen.

D. h., In dem Beispielcode verwenden Sie einfach Derived* als den Typ des Zeigers.

+1

Während ich von ganzem Herzen dem letzten Satz zustimme, bedeutet das auch, dass man Polymorphismus * nicht macht. – SergeyA

2

Bei mehrfacher Vererbung besteht das Objekt Derived aus zwei Unterobjekten, einem Base und einem Unrelated. Der Compiler weiß, wie er auf den Teil des Objekts zugreifen kann, der benötigt wird, normalerweise durch Hinzufügen eines Offsets zum Zeiger (aber das ist ein Implementierungsdetail). Es kann nur tun, wenn es den tatsächlichen Typ eines Zeigers kennt. Wenn Sie einen C-Style-Cast verwenden, haben Sie den Compiler angewiesen, den tatsächlichen Typ zu ignorieren und diesen Zeigerwert als Zeiger auf etwas anderes zu behandeln. Es verfügt nicht mehr über die erforderlichen Informationen, um ordnungsgemäß auf das gewünschte Unterobjekt zuzugreifen, und es schlägt fehl.

dynamic_cast ermöglicht dem Compiler, Laufzeitinformationen über das Objekt zu verwenden, um das richtige Unterobjekt zu finden, das darin enthalten ist. Wenn Sie die Zeigerwerte selbst ausgeben oder untersuchen würden, würden Sie sehen, dass sie unterschiedlich sind.