2010-12-22 6 views
23

Es ist mir einfach passiert, dass ich mich gefragt habe, wie Ressourcen im folgenden Fall freigegeben werden.Was passiert mit dem Destruktor der Basisklasse, wenn ein abgeleiteter Klassendestruktor eine Exception auslöst

class Base { 
    Resource *r; 

public: 
    Base() { /* ... */ } 
    ~Base() { 
    delete r; 
    } 
}; 

class Derived : public Base { 
public: 
    Derived() { /* ... */ } 
    ~Derived() { 
    /* Suddenly something here throws! */ 
    } 
}; 

int main() { 
    try { 
    Derived d; 
    } catch(...) { 
    /* what happened with Base::r !? */ 
    } 
} 

Wird der Basisklassendestruktor aufgerufen, wenn der abgeleitete Klassendestruktor auswirft? Oder wird es ein Leck geben?

+6

Aus dem Gedächtnis, der Standard der Definitionen dessen, was geschieht, wenn ein destructor Würfe sind so unglaublich komplex und in der Regel schrecklich, dass niemand jemals, jemals von einem destructor wirft. Ich bin auch ziemlich sicher, dass alle Standard-Container davon ausgehen, dass Destruktoren nicht durchgehen. Grundsätzlich, obwohl ich technisch denke, ist es legal und definiert, die Realität ist, dass niemand es tut und aus einem sehr guten Grund, also tu es nie. – Puppy

+1

Die Probe, die Sie dort haben, sollte kein Problem sein. d geht nur normal aus dem Rahmen. Wo es dreckig werden könnte, ist, dass der Grund dafür ist, dass der Stapel im Zuge der Handhabung einer weiteren Ausnahme wieder abgewickelt wird. Diese Möglichkeit führt zu Faustregeln darüber, niemals einen Destruktor einzuwerfen. –

Antwort

20

Nach §15.2/2:

Ein Objekt, das teilweise aufgebaut ist, wird teilweise oder Destruktoren für alle seine vollständig aufgebaut Subobjekte ausgeführt haben, zerstört werden, das heißt, für die Subobjekte, für die der Konstruktor Ausführung abgeschlossen hat und der Destruktor hat noch nicht mit der Ausführung begonnen.

So sollte der Basisklasse-Destruktor aufgerufen werden. Das heißt, wie wir wissen, das wird die Basisklasse aufzuräumen:

#include <iostream> 

struct foo 
{ 
    ~foo() 
    { 
     std::cout << "clean" << std::endl; 
    } 
}; 

struct bar : foo 
{ 
    bar() 
    { // foo is initialized... 
     throw 0; // ...so its destructor is run 
    } 
}; 

int main() 
{ 
    try 
    { 
     bar b; 
    } 
    catch (...) 
    { 
     std::cerr << "caught" << std::endl; 
    } 
} 

Und dass dies das Mitglied aufzuräumen:

#include <iostream> 

struct foo 
{ 
    ~foo() 
    { 
     std::cout << "clean" << std::endl; 
    } 
}; 

struct bar 
{ 
    ~bar() 
    { // f has been initialized... 
     throw 0; // ...so its destructor will run 
    } 

    foo f; 
}; 

int main() 
{ 
    try 
    { 
     bar b; 
    } 
    catch (...) 
    { 
     std::cerr << "caught" << std::endl; 
    } 
} 

Dies wird auch die Basisklasse aufzuräumen:

#include <iostream> 

struct foo 
{ 
    ~foo() 
    { 
     std::cout << "clean" << std::endl; 
    } 
}; 

struct bar : foo 
{ 
    ~bar() 
    { // foo has been initialized... 
     throw 0; // ...so its destructor will run 
    } 
}; 

int main() 
{ 
    try 
    { 
     bar b; 
    } 
    catch (...) 
    { 
     std::cerr << "caught" << std::endl; 
    } 
} 

Das ist mein Verständnis des Zitats.

+0

In VC++ 9 Base Destruktor wird in der Tat aufgerufen. – sharptooth

+0

@sharp: Und in MSVC10. @ Kirill: Danke, ich bin rostig. :) – GManNickG

+1

Danke für die Antwort. Ich denke, das ist genau richtig! –

1

Der Basisdestruktor wird aufgerufen.

In Effektiv C++ empfiehlt Meyers, dass Ausnahmen Destruktoren nicht verlassen sollten. Fangen Sie die Ausnahme innerhalb des Destruktors und behandeln Sie es, schlucken Sie es oder beenden Sie es.

+0

Haben Sie eine Begründung für Ihren Anspruch? – GManNickG

+1

Sie schreiben "Ich denke nicht". Und ich denke schon. Und was? OP hat einen sehr hohen Reputationswert und will offensichtlich eine klarere Antwort bekommen. –

+0

Ich schrieb "Ich denke nicht", denn das habe ich verstanden. Ich habe es jetzt überprüft, und ja, die Basistreduktoren werden tatsächlich aufgerufen. Es gibt immer noch ein Problem darüber, wie viel von dem abgeleiteten Klassendestruktor vor der Ausnahme ausgeführt wurde. – DanS

1

Die Basisklasse Destruktor wird in der Tat aufgerufen. Beispielcode:

#include 
#include 

class Base { 
public: 
    Base() { /* ... */ } 
    ~Base() { 
    printf("Base\n"); 
    } 
}; 

class Derived : public Base { 
public: 
    Derived() { /* ... */ } 
    ~Derived() { 
    printf("Derived\n"); 
    throw 1; 
    } 
}; 

int main() { 
    try { 
    Derived d; 
    } catch(...) { 
    printf("CAUGHT!\n"); 
    } 
} 

Diese Drucke:

Derived 
Base 
CAUGHT! 
+1

Gut. Aber GMan hat vorher geantwortet. –

+1

@Alexey - es ist kein Rennen, es ist die Qualität der Antwort, die zählt. –

+0

@Brian: Nicht mein eigenes Horn, aber da meine Antwort schon existierte, gibt es wirklich nichts mehr hinzuzufügen, um die Frage direkt zu beantworten. Wahrscheinlich ist diese Antwort schlechter, weil es nur ein Beweis dafür ist, dass ein Compiler unter dieser spezifischen Bedingung eine bestimmte Ausgabe produziert hat, nicht als Beweis, dass es garantiert ist. – GManNickG

Verwandte Themen