一番上の答えは間違っていて、それでもメモリリークすると思います。コンストラクターが例外をスローした場合、クラス メンバーのデストラクタは呼び出されません(コンストラクターが初期化を完了しておらず、一部のメンバーがコンストラクター呼び出しに達していない可能性があるため)。それらのデストラクタは、クラスのデストラクタ呼び出し中にのみ呼び出されます。それは理にかなっています。
この単純なプログラムはそれを示しています。
#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;
}
次の出力 (g++ 4.5.2 を使用):
A constructor [3]
A constructor [5]
B constructor
terminate called after throwing an instance of 'char const*'
Aborted
コンストラクターが途中で失敗した場合、それに対処するのはあなたの責任です。さらに悪いことに、基本クラスのコンストラクターから例外がスローされる可能性があります。これらのケースに対処する方法は、「関数 try ブロック」を使用することです (ただし、その場合でも、部分的に初期化されたオブジェクトの破棄を慎重にコーディングする必要があります)。
問題に対する正しいアプローチは、次のようになります。
#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;
}
実行すると、割り当てられたオブジェクトのみが破棄されて解放される、期待される出力が得られます。
B constructor
A constructor [3]
B Cleanup
A destructor [3]
terminate called after throwing an instance of 'char const*'
Aborted
必要に応じて、追加のコピーを使用して、スマート共有ポインターを使用して解決できます。次のようなコンストラクタを作成します。
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;
}
}
がんばれ、ツヴィ。