Früher dachte ich, wenn in C ++ ein Konstruktor eine Ausnahme auslöst, wird der Destruktor dieser "teilweise konstruierten" Klasse nicht aufgerufen.
Aber es scheint, dass es in C ++ 11 nicht mehr stimmt: Ich habe den folgenden Code mit g ++ kompiliert und er druckt " X destructor
" auf die Konsole. Warum ist das?
#include <exception>
#include <iostream>
#include <stdexcept>
using namespace std;
class X
{
public:
X() : X(10)
{
throw runtime_error("Exception thrown in X::X()");
}
X(int a)
{
cout << "X::X(" << a << ")" << endl;
}
~X()
{
cout << "X destructor" << endl;
}
};
int main()
{
try
{
X x;
}
catch(const exception& e)
{
cerr << "*** ERROR: " << e.what() << endl;
}
}
Ausgabe
Standard out:
X::X(10)
X destructor
Standard error:
*** ERROR: Exception thrown in X::X()
Antworten:
Das Delegieren von Konstruktoren ist in der Tat eine neue Funktion, die eine neue Zerstörungslogik einführt.
Lassen Sie uns die Lebensdauer eines Objekts erneut betrachten: Die Lebensdauer eines Objekts beginnt, wenn ein Konstruktor fertig ist. (Siehe 15.2 / 2. Der Standard nennt dies den "Hauptkonstruktor".) In Ihrem Fall ist dies der Konstruktor
X(int)
. Der zweite delegierende KonstruktorX()
fungiert jetzt nur noch als einfache Elementfunktion. Beim Abwickeln des Bereichs werden die Destruktoren aller vollständig konstruierten Objekte aufgerufen, einschließlichx
.Die Auswirkungen davon sind tatsächlich ziemlich tiefgreifend: Sie können jetzt "komplexe" Arbeitslasten in einen Konstruktor einfügen und die übliche Ausnahmeverbreitung voll ausnutzen, solange Sie Ihren Konstruktor an einen anderen Konstruktor delegieren. Ein solches Design kann die Notwendigkeit verschiedener "init" -Funktionen überflüssig machen, die früher immer dann beliebt waren, wenn nicht gewünscht wurde, zu viel Arbeit in einen regulären Konstruktor zu stecken.
Die spezifische Sprache, die das angezeigte Verhalten definiert, ist:
quelle
[C++11: 15.2/2]
Es ist immer noch wahr. Seit C ++ 03 hat sich nichts geändert (für einen Wert von nichts ;-))
Was Sie dachten, ist immer noch wahr, aber es gibt kein teilweise konstruiertes Objekt, wenn die Ausnahme ausgelöst wird.
Der C ++ 03 TC1-Standard sagt (Hervorhebung von mir):
dh Jedes Objekt, das seinen Konstruktor fertiggestellt hat, wird durch Ausführen des Destruktors zerstört. Das ist eine schöne einfache Regel.
Grundsätzlich gilt in C ++ 11 dieselbe Regel: Sobald
X(int)
der Objektkonstruktor die Ausführung abgeschlossen hat, ist er vollständig konstruiert, sodass er vollständig konstruiert ist und sein Destruktor zum richtigen Zeitpunkt ausgeführt wird (wenn er den Gültigkeitsbereich verlässt oder ein Die Ausnahme wird in einem späteren Stadium des Aufbaus ausgelöst.) Es ist im Wesentlichen immer noch dieselbe Regel.Der Hauptteil des delegierenden Konstruktors läuft dem anderen Konstruktor nach und kann zusätzliche Arbeit leisten. Dies ändert jedoch nichts an der Tatsache, dass die Konstruktion des Objekts abgeschlossen ist, sodass es vollständig erstellt wird. Der delegierende Konstruktor ist analog zu einem Konstruktor einer abgeleiteten Klasse, der nach Abschluss eines Konstruktors einer Basisklasse mehr Code ausführt. In gewissem Sinne können Sie Ihr Beispiel folgendermaßen betrachten:
class X { public: X(int a) { cout << "X::X(" << a << ")" << endl; } ~X() { cout << "X destructor" << endl; } }; class X_delegating : X { public: X_delegating() : X(10) { throw runtime_error("Exception thrown in X::X()"); } };
Es ist nicht wirklich so, es gibt nur einen Typ, aber es ist insofern analog, als der
X(int)
Konstruktor ausgeführt wird, dann wird zusätzlicher Code im delegierenden Konstruktor ausgeführt, und wenn dies dieX
"Basisklasse" auslöst (die eigentlich keine Basisklasse ist) wird zerstört.quelle