2016-04-20 21 views
2

Ich erkannte, dass diese Methode besonders nützlich ist, wenn Code debuggt, wenn ich eine externe Bibliothek nicht ändern/erstellen möchte.Statisches Casting auf falschen Typ

Bin noch meine Hände damit zu verbrennen oder ist das zumindest für das Debuggen sicher?

http://ideone.com/i9jBN8

#include <iostream> 
using namespace std; 

class A 
{ 
public: 
    A(int lx):x(lx){}; 
protected: 
    int x; 
}; 

class B : public A 
{ 
public: 

    void print(){std::cout << x << endl;} 
}; 

int main() { 
    A *a = new A(22); 
    B *b = static_cast<B*>(a); 
    b->print(); 
} 

Edit: was ist, wenn die Verwendung Ich verwende reinterpret_cast statt static_cast und stellen Sie sicher, dass B keine virtuellen Methoden hat, würde nicht die Erinnerung an A und B Spiel und es wird nicht sein ein UB richtig?

+0

Also, was ist Ihre Frage? – teivaz

+0

> Bin noch meine Hände damit verbrennen oder ist das zumindest sicher zum Debuggen? – tejas

+0

Der Aufruf von 'b-> print()' ist formal undefiniertes Verhalten, da 'b' nicht auf ein' B' zeigt. Der einzige Weg, um sicher zu gehen, dass es für das Debugging sicher ist, ist das Testen mit Ihrer speziellen Werkzeugkette. Es wäre besser, einige Fähigkeiten in Klasse "A" zur Verfügung zu stellen, die für Debugging-Zwecke verwendet werden können. – Peter

Antwort

2

Ihr Code hat ein undefiniertes Verhalten, aber da es ziemlich einfach ist, können Sie sich wahrscheinlich überzeugen, ob es an Ihrer Implementierung funktioniert, indem Sie den Code der Zwischenversion oder die disassemblierte Binärdatei untersuchen.

Die Chancen stehen gut, dass es in der Praxis funktioniert, besonders für einfache Klassen. Die Hauptangst mit solchen "Es sieht so aus, als ob es funktionieren sollte, auch wenn es UB ist" -Situationen ist, dass der Optimierer einige sehr schlaue Schlussfolgerungen basierend auf einer Annahme machen wird, die er zu machen berechtigt ist, was aber nicht wahr ist UB-Code. Wenn Sie also alle Optimierungen ausschalten, erhalten Sie eine bessere Chance, (Des-) Assemblierung zu sehen, wenn (a) Ihr Ziel erreicht und (b) Sie einen Sinn ergeben.

Für dieses Beispiel zumindest, können Sie den Wert von x für Debugging-Zwecke zugreifen, ohne undefiniertes Verhalten und ohne die Klasse zu modifizieren A, indem Sie eine Kopie unter:

class B : public A 
{ 
public: 
    B(const A&a) : A(a) {} 

    void print(){std::cout << x << endl;} 
}; 

int main() { 
    A *a = new A(22); 
    static_cast<B>(*a).print(); 
} 
+0

Was ist, wenn ich reinterpret_cast anstelle von static_cast verwende und sicherstelle, dass B keine virtuellen Methoden hat, würden die Daten von A und B nicht übereinstimmen und es wäre kein UB-Recht? – tejas

+0

@tejas: immer noch UB. Das einzige, was Sie portabel mit einem Zeiger machen können, der 'reininterpret_cast' ist, ist, ihn auf den ursprünglichen Typ zurückzuspielen. –

+0

Btw, da 'A' und' B' beide Standard-Layout-Klassen seit C++ 11 sind, ist es * garantiert, dass 'x' in beiden" an der gleichen Stelle "ist, das heißt, dass der Offset von 'x' von' A * 'ist das gleiche wie der Offset von' x' von 'B *'. Sie haben also recht, dass die Daten übereinstimmen. Aber ich rufe diese UB an, weil AFAIK das * noch * nicht ausreicht, um definiertes Verhalten nach dem Standard zu bieten. Die strengen Aliasregeln in [basic.lval] erlauben es Ihnen nicht, auf ein Objekt zu verweisen, das eine abgeleitete Klasse seines dynamischen Typs verwendet, obwohl sie das gleiche Layout haben. –

2

Nein, das ist nicht sicher, in der Tat ist es undefined behavior. Wenn Sie einen Basiszeiger auf einen abgeleiteten Zeiger umwandeln möchten, sollten Sie dynamic_cast als nullptr verwenden, wenn die Konvertierung ungültig ist.

+0

Es macht nichts, wenn 'print()' alles tut, was 'B' erfordert. Das macht das Problem nur sichtbar. Diese "geschickte" Art des Affepatchings ist bereits nach dem static_cast undefiniert. – dhke