Aufruf von Destruktoren bei Exception
-
Hallo,
ich habe zwei etwas speziellere Fragen zum Aufruf von Destruktoren bzw. stack unwinding beim Werfen von Exceptions. Gegeben sei folgender Code:
try { A a; B b(a); // a wird als Member von b gespeichert b.foo(); } catch(...) { cerr << "Fehler"; }1. Was passiert, wenn foo() eine Exception wirft? Wird dann der Destruktor erst für a als Member von b und dann ein weiteres Mal für a als lokale Variable im try-Block aufgerufen?
2. Was passiert, wenn bereits der Konstruktur von b eine Exception wirft? Der Destruktor von b wird dann schon einmal nicht aufgerufen, soviel weiß ich bereits, aber was ist mit dem von a?
Mich interessiert vor allem die genaue Reihenfolge von Destruktoraufrufen und Ausführung des catch-Blocks. Ich hoffe, dass man das anhand des Beispiels überhaupt sicher sagen kann, ansonsten müssten noch weitere Angaben gemacht werden.
-
2. Was passiert, wenn bereits der Konstruktur von b eine Exception wirft? Der Destruktor von b wird dann schon einmal nicht aufgerufen, soviel weiß ich bereits, aber was ist mit dem von a?
der von a wird aufgerufen, da a schon konstruiert wurde.
1. Was passiert, wenn foo() eine Exception wirft? Wird dann der Destruktor erst für a als Member von b und dann ein weiteres Mal für a als lokale Variable im try-Block aufgerufen?
ja, A::~A wird 2x ausgeführt, wenn B ein A beinhaltet (dann wurde es im CTor ja kopiert). Wenn A nur eine Referenz oder einen Pointer hält, wird der DTor logischerweise nur einmal aufgerufen.
Reihenfolge, wenn foo wirft (editiert - da war ich wohl nicht recht bei der sache^^):
foo() B::~B { A::~A {} } A::~A {}wenn es an dem bsp noch nicht klar wurde:
(die zu erst erstellten Objekte werden zu letzt zerstört)
gegeben sei:class A { A() {std::cout << "A::A()" << std::endl;} ~A() {std::cout << "A::~A()" << std::endl;} }; class B { B() {std::cout << "B::B()" << std::endl;} ~B() {std::cout << "B::~B()" << std::endl;} }; class C { C() {std::cout << "C::C()" << std::endl;} ~C() {std::cout << "C::~C()" << std::endl;} };anwendung:
void foo() { std::cout << "foo()" << std::endl; throw 0; /*unreachable code*/ } int main() { try { A a; B b; C c; foo(); } catch(...) { std::cerr << "cought" << std::endl; } }ausgabe wäre:
A::A() B::B() C::C() foo() C::~C() B::~B() A::~A() coughtbb
-
Danke, das macht die ganze Sache schon wesentlich klarer
Das heißt also, um bei meinem Beispiel zu bleiben, dass bei folgender Definition der Klasse B und einer Exception in foo() zuerst der Destruktor des Members a_, dann der Destruktor der lokalen Variable b im try-Block, dann jener der lokalen Variable a im try-Block und erst dann der catch-Block ausgeführt wird?class B { public: B(A a) : a_(a) {} void foo(); private: A a_; };Oder würde A::~A hier sogar dreimal ausgeführt, da der Kopierkonstruktor zwei neue Objekte erzeugt?
-
Mach doch eine Ausgabe in den Destruktoren und probier es aus.