16 Stimmen

Wird der Speicher freigegeben, wenn ein Destruktor aufgerufen wird oder wenn `delete` aufgerufen wird?

Angenommen, Sie haben ein Objekt der class Fool .

class Fool
{
    int a,b,c;
    double* array ;
    //...
    ~Fool()
    {
        // destroys the array..
        delete[] array ;
    }
};

Fool *fool = new Fool() ;

Jetzt, Ich weiß, Sie sollten nicht aber irgendein Idiot ruft den Destruktor auf fool sowieso. fool->~Fool(); .

Heißt das fool Der Speicher wird freigegeben (d.h. a,b,c sind ungültig) oder bedeutet das, dass nur alle Deallokationen in ~Fool() Funktion auftreten (d.h. das Array wird nur gelöscht?)

Ich schätze, meine Frage ist, ist ein Destruktor nur eine weitere Funktion die aufgerufen wird, wenn delete für ein Objekt aufgerufen wird, oder tut es mehr?

32voto

templatetypedef Punkte 343693

Wenn Sie schreiben

fool->~Fool();

Sie beenden die Lebensdauer des Objekts, was den Destruktor aufruft und die inneren array Array. Der Speicher, in dem sich das Objekt befindet, wird jedoch nicht freigegeben, d.h. wenn Sie das Objekt mit Hilfe von placement new wieder zum Leben erwecken wollen:

new (fool) Fool;

können Sie dies tun.

Gemäß der Spezifikation ist das Lesen oder Schreiben der Werte der Felder von fool nachdem Sie den Destruktor explizit aufgerufen haben, führt zu einem undefinierten Verhalten, da die Lebensdauer des Objekts beendet ist, aber der Speicher, in dem sich das Objekt befindet, sollte immer noch alloziert sein, und Sie müssen ihn durch den Aufruf von operator delete :

fool->~Fool();
operator delete(fool);

Der Grund für die Verwendung operator delete statt nur zu schreiben

delete fool;

ist, dass letztere ein undefiniertes Verhalten aufweist, da fool Die Lebenszeit des Kindes ist bereits abgelaufen. Verwendung der rohen Deallokationsroutine operator delete stellt sicher, dass der Speicher zurückgewonnen wird, ohne dass versucht wird, die Lebensdauer des Objekts zu beenden.

Natürlich, wenn der Speicher für das Objekt nicht von new (vielleicht ist sie auf dem Stapel gespeichert, oder Sie verwenden einen benutzerdefinierten Allokator), dann sollten Sie nicht operator delete um sie zu befreien. Wenn Sie das täten, würden Sie (wieder!) mit undefiniertem Verhalten enden. Dies scheint ein wiederkehrendes Thema in dieser Frage zu sein :-)

Ich hoffe, das hilft!

10voto

Kerrek SB Punkte 445528

Der Aufruf des Destruktors tut genau das, er ruft den Destruktor auf. Nicht mehr und nicht weniger. Die Zuweisung ist getrennt von der Konstruktion und die Freigabe von der Zerstörung.

Der typische Ablauf ist wie folgt:

1. Allocate memory
2. Construct object
3. Destroy object  (assuming no exception during construction)
4. Deallocate memory

Wenn Sie diesen Vorgang manuell ausführen, werden Sie in der Tat haben um den Destruktor selbst aufzurufen:

void * addr = ::operator new(sizeof(Fool));
Fool * fp = new (addr) Fool;
fp->~Fool();
::operator delete(addr);

Die automatische Schreibweise dafür ist natürlich Fool * fp = new Fool; delete fp; . Die new Ausdruck ruft die Zuweisung und Konstruktion für Sie auf, und der delete Ausdruck ruft den Destruktor auf und gibt den Speicher frei.

3voto

In silico Punkte 49539

Bedeutet das, dass der Speicher von fool freigegeben ist (d.h. a,b,c sind ungültig) oder bedeutet bedeutet das, dass nur die Deallokationen in der Funktion ~Fool() stattfinden (d.h. nur das Array wird gelöscht?)

Fool::~Fool() hat keinerlei Kenntnis darüber, ob die Fool Instanz wird im dynamischen Speicher gespeichert (über new ) oder ob es im automatischen Speicher (d.h. Stack-Objekte) gespeichert ist. Da das Objekt nicht mehr existiert, nachdem der Destruktor ausgeführt wurde, kann man nicht davon ausgehen, dass a , b , c y array bleibt auch nach Beendigung des Destruktors gültig.

Da jedoch Fool::~Fool() weiß nichts darüber, wie die Fool zugewiesen wurde, der Aufruf des Destruktors direkt auf einer new -zugewiesen Fool gibt nicht den zugrunde liegenden Speicher frei, der dem Objekt zugrunde liegt.

0voto

Chris Jester-Young Punkte 212385

Sie sollten no Zugang a , b y c nachdem der Destruktor aufgerufen wurde, auch wenn es sich um einen expliziten Destruktoraufruf handelt. Man weiß nie, was der Compiler in den Destruktor einfügt, das diese Werte ungültig machen könnte.

Im Falle eines expliziten Destruktoraufrufs wird der Speicher jedoch nicht tatsächlich freigegeben. Dies ist beabsichtigt; es erlaubt einem Objekt, das mit Hilfe der Platzierung new zu bereinigen.

Beispiel:

char buf[sizeof (Fool)];
Fool* fool = new (buf) Fool;  // fool points to buf
// ...
fool->~Fool();

0voto

Keith Punkte 6658

Der einfachste Ort, um zu sehen, dass der Destruktor sich von der Deallokation unterscheidet, ist delete ist, wenn die Zuteilung von vornherein automatisch erfolgt:

{
  Fool fool;
  // ~Fool called on exit from block; nary a sign of new or delete
}

Beachten Sie auch, dass die STL-Container den expliziten Destruktoraufruf voll ausnutzen. Zum Beispiel kann ein std::vector<> behandelt die Lebenszeit von Speichern und enthaltenen Objekten völlig getrennt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X