2014-06-09 17 views
8

Wie gezeigt here kann man dynamic_cast verwenden einen gelöschten Zeiger zu erfassen:Wie wird die Löschung eines Zeigers erfasst mittels dynamischer Guss

#include <iostream> 

using namespace std; 

class A 
{ 
public: 
    A() {} 
    virtual ~A() {} 
}; 

class B : public A 
{ 
public: 
    B() {} 
}; 

int main() 
{ 
    B* pB = new B; 

    cout << "dynamic_cast<B*>(pB) "; 
    cout << (dynamic_cast<B*>(pB) ? "worked" : "failed") << endl; 

    cout << "dynamic_cast<B*>((A*)pB) "; 
    cout << (dynamic_cast<B*>((A*)pB) ? "worked" : "failed") << endl; 

    delete pB; 

    cout << "dynamic_cast<B*>(pB) "; 
    cout << (dynamic_cast<B*>(pB) ? "worked" : "failed") << endl; 

    cout << "dynamic_cast<B*>((A*)pB) "; 
    cout << (dynamic_cast<B*>((A*)pB) ? "worked" : "failed") << endl; 

} 

die Ausgabe:

dynamic_cast<B*>(pB) worked 
dynamic_cast<B*>((A*)pB) worked 
dynamic_cast<B*>(pB) worked 
dynamic_cast<B*>((A*)pB) failed 

Es wird erläutert, dass Das Löschen der Vtable wird erkannt.

Aber ich frage mich, wie ist das möglich, da wir den freigegebenen Speicher nicht überschreiben?

Und ist diese Lösung vollständig tragbar?

Dank

+0

Ich rate 'dynamic_cast ' verwendet [RTTI] (http://en.wikipedia.org/wiki/Run-time_type_information), um den Typ zu bestätigen, der umgewandelt wird. –

+0

Ich vermute, dass es plattformabhängiges Verhalten ist. –

+0

Visual Studio 2013 löst mit diesem Code eine '__non_rtti_object'-Ausnahme aus. –

Antwort

6

Zunächst einmal versuchen, ein gelöschtes Objekt in jeder Form führt zu undefiniertem Verhalten zu verwenden: was auch immer Sie gerade sehen führen könnte!

Der Grund für das beobachtete Verhalten ist einfach, dass ein Objekt den Typ während der Zerstörung ändert: von einem Objekt des konkreten Typs ändert es sich durch alle Arten in der Hierarchie. An jedem Punkt ändern sich die virtuellen Funktionen und die vtable (oder ähnlich) wird ersetzt. Die dynamic_cast<...>() erkennt diese Änderung in den Bytes, die an der Position des Objekts nur gespeichert werden, einfach.

Wenn Sie möchten, dass diese Technik nicht zuverlässig funktioniert, können Sie den Inhalt des gelöschten Speichers auf ein zufälliges Bitmuster oder das Bitmuster eines Objekts des am meisten abgeleiteten Typs setzen: ein zufälliges Bit Muster ergibt wahrscheinlich einen Unfall und memcpy() behauptet wahrscheinlich, dass das Objekt Stillleben ist. Natürlich, da es undefiniertes Verhalten ist, kann alles passieren.

Ein entsprechender Abschnitt auf diesem 3.8 [basic.life] Ziffer 5:

Bevor die Lebensdauer eines Objekts begonnen hat nach der Lagerung aber das Objekt zugeordnet wurde oder wird occupy, nach der Laufzeit ein Objekt ist beendet und bevor der Speicher, den das Objekt belegt hat, wiederverwendet oder freigegeben wird, kann jeder Zeiger, der auf den Speicherort verweist, wo sich das Objekt befindet oder befand, verwendet werden, aber nur in beschränkter Weise. Für ein Objekt im Aufbau oder Zerstörung, siehe 12.7. Sonst verweist ein solcher Zeiger auf den zugeordneten Speicher (3.7.4.2) und verwendet den Zeiger, als ob der Zeiger vom Typ void*, , wohldefiniert wäre. Die Indirektion durch einen solchen Zeiger ist erlaubt, aber der resultierende L-Wert kann nur in begrenzter Weise verwendet werden, wie nachstehend beschrieben. Das Programm hat ein undefiniertes Verhalten, wenn:

  • ...
  • der Zeiger als Operand eines dynamic_cast (5.2.7) verwendet wird.
  • ...

Merkwürdig ist, dass das Beispiel auf der letzten Kugel auf dynamic_cast nicht dynamic_cast verwenden.

Natürlich ist das Objekt wahrscheinlich auch freigegeben, in diesem Fall gelten die oben genannten Garantien nicht einmal.