我相信最佳答案是错误的,并且仍然会泄漏内存。如果构造函数抛出异常,则不会调用类成员的析构函数(因为它从未完成初始化,并且可能某些成员从未到达其构造函数调用)。它们的析构函数仅在类的析构函数调用期间被调用。这才有意义。
这个简单的程序演示了它。
#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
如果您的构造函数中途失败,那么您有责任处理它。更糟糕的是,异常可能会从您的基类的构造函数中抛出!处理这些情况的方法是使用“函数尝试块”(但即便如此,您也必须仔细编写部分初始化对象的销毁代码)。
解决问题的正确方法是这样的:
#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;
}
}
祝你好运,茨维。