我正在阅读来自C++ FAQ的失败构造函数,并且不理解以下代码。
void f()
{
X x; ← if X::X() throws, the memory for x itself will not leak
Y* p = new Y(); ← if Y::Y() throws, the memory for *p itself will not leak
}
如果构造函数抛出,p指向的内存怎么可能不会泄漏?我假设序列如下。
答案 0 :(得分:2)
如果Y的构造函数抛出,则堆栈被解开,包括删除为Y分配的内存。
问题主要发生在/如果您有多个对象要处理的情况下。例如:
void f() {
X *x = new X();
Y *y = new Y();
}
现在,如果new X()
部分成功,但new Y()
部分失败,则会删除为y
分配的内存,但 {{1}不会被破坏,它的记忆将被泄露。如果您真的坚持,可以使用x
块解决此问题:
try
这个问题的一大问题是,如果你有两个以上的项目,你必须嵌套try {
X *x = new X();
Y * y = new Y();
}
catch (y_construction_failed) {
delete x;
}
块,所以如果你需要,比如说,有六个局部变量,那么它将被深深地嵌套并且非常难以置信。难看。
答案 1 :(得分:0)
重要的是要注意,即使删除了对象,在这种情况下也不会调用它的析构函数。
这是有道理的,因为构造期间的异常表明对象从未完全构造(即,它的类不变量尚未建立),因此调用析构函数可能是危险的。
这样做的缺点是,如果构造函数执行需要通常由析构函数执行清理的操作,那么现在构造函数的负责在异常情况下执行此清理。请看以下示例:
class C {
private:
int* p1;
int* p2;
public:
C() : p1(new int()), p2(new int()) {}
~C() { delete p1; delete p2; }
};
如果分配p2
抛出,则已为p1
分配的内存将泄漏。作为程序员,您有责任以不会发生这种情况的方式编写构造函数。
实现这一目标的最简单方法是将资源管理职责委托给像unique_ptr
这样的RAII容器类。这样,没有类负责管理多个资源,并且不再出现上述情况。
答案 2 :(得分:0)
你会遇到类似的问题,函数void f(X *,Y *)并调用f(new X(),new Y())。如果其中一个新调用成功而另一个失败则会导致内存泄漏。要解决它,您可以创建其他函数'X * make_X()'和'Y * make_Y()'返回指针。现在,f(make_X(),make_Y())是安全的。 (到目前为止,你可能会使用智能指针)
答案 3 :(得分:0)
一切都取决于Y
的构造函数。该示例假设构造函数已定义良好,它在抛出之前处理构造函数内的所有内容。
使用RAII和智能指针(如std::unique_ptr
)有助于从构造函数中编写安全抛出。例如:
struct Foo
{
Foo() : a(new A)
{
throw std::runtime_error("test");
}
std::unique_ptr<A> a;
};
a
将被安全清理。