Ich glaube, dass die Top-Antwort falsch ist und nach wie vor ein Speicherleck würde. Der Destruktor für die Klassenmitglieder wird nicht aufgerufen werden, wenn der Konstruktor eine Ausnahme auslöst (weil es nie seine Initialisierung abgeschlossen hat und vielleicht einige Mitglieder ihre Konstruktoraufrufe nie erreicht haben). Ihre Destruktoren werden nur während des Destruktoraufrufs der Klasse aufgerufen. Das macht nur Sinn.
Dieses einfache Programm demonstriert es.
#include <stdio.h>
class A
{
int x;
public:
A(int x) : x(x) { printf("A constructor [%d]\n", x); }
~A() { printf("A destructor [%d]\n", x); }
};
class B
{
A a1;
A a2;
public:
B()
: a1(3),
a2(5)
{
printf("B constructor\n");
throw "failed";
}
~B() { printf("B destructor\n"); }
};
int main()
{
B b;
return 0;
}
Mit der folgenden Ausgabe (mit g ++ 4.5.2):
A constructor [3]
A constructor [5]
B constructor
terminate called after throwing an instance of 'char const*'
Aborted
Wenn Ihr Konstruktor partway dann scheitert es liegt in Ihrer Verantwortung, damit umzugehen. Schlimmer noch, die Ausnahme kann vom Konstruktor der Basisklasse ausgelöst werden! Der Weg, um mit diesen Fällen umzugehen, besteht darin, einen "Funktionsversuchsblock" zu verwenden (aber selbst dann müssen Sie die Zerstörung Ihres teilweise initialisierten Objekts sorgfältig codieren).
Der richtige Ansatz, um Ihr Problem so etwas wie dieses wäre dann:
#include <stdio.h>
class A
{
int x;
public:
A(int x) : x(x) { printf("A constructor [%d]\n", x); }
~A() { printf("A destructor [%d]\n", x); }
};
class B
{
A * a1;
A * a2;
public:
B()
try // <--- Notice this change
: a1(NULL),
a2(NULL)
{
printf("B constructor\n");
a1 = new A(3);
throw "fail";
a2 = new A(5);
}
catch (...) { // <--- Notice this change
printf("B Cleanup\n");
delete a2; // It's ok if it's NULL.
delete a1; // It's ok if it's NULL.
}
~B() { printf("B destructor\n"); }
};
int main()
{
B b;
return 0;
}
Wenn Sie es laufen Sie die erwartete Ausgabe, wo nur die zugeordneten Objekte werden zerstört und befreit.
Sie können es immer noch mit intelligenten freigegebenen Zeigern arbeiten, wenn Sie möchten, mit zusätzlichen Kopieren. Schreiben Sie einen Konstruktor wie folgt aus:
class C
{
std::shared_ptr<someclass> a1;
std::shared_ptr<someclass> a2;
public:
C()
{
std::shared_ptr<someclass> new_a1(new someclass());
std::shared_ptr<someclass> new_a2(new someclass());
// You will reach here only if both allocations succeeded. Exception will free them both since they were allocated as automatic variables on the stack.
a1 = new_a1;
a2 = new_a2;
}
}
Viel Glück, Tzvi.
Ich weiß, ich habe eine schreckliche Natur, die ich gegen Nitpick nicht widerstehen kann. Ich kann nicht anders. Meine 2 Cent: Anweisung objsomeclass = someclass(); ist unnötig. Im Rumpf des Konstruktors ist objsomeclass bereits standardmäßig initialisiert. objsomeclass (someclass()) unten macht auch keinen Sinn. –
Ich stimme zu, aber denke, dass eine Klasse einen expliziten Konstruktor hat. Und ich wollte auf das Objekt konzentrieren wird im Konstruktor erstellt – yesraaj
Ja, ich weiß, es ist nur ein Beispiel. Deshalb nannte ich es pingelig. BTW Konstruktor base() könnte öffentlich sein :) –