2

我正在阅读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指向的内存怎么可能不会泄漏?我假设顺序如下。

  1. 为对象 Y 分配内存。
  2. Y 的构造函数被调用。
  3. Y 的构造函数抛出和 p 指向的内存泄漏。
4

4 回答 4

2

如果 Y 的构造函数抛出,则堆栈被展开,包括删除为 Y 分配的内存。

问题主要出现在/如果您有多个对象要处理时。例如:

void f() { 
    X *x = new X();
    Y *y = new Y();
}

现在,如果new X()部分成功,但new Y()部分失败,分配给的内存y将被删除, x不会被销毁,并且它的内存将被泄漏。如果您真的坚持,您可以使用块解决此问题:try

try { 
    X *x = new X();

    Y * y = new Y();
}
catch (y_construction_failed) {
    delete x;
}

这样做的最大问题是,try如果你有两个以上的项目,你必须嵌套块,所以如果你需要,比如说,六个局部变量,它将会被深度嵌套并且非常难看。

于 2013-11-12T16:28:19.430 回答
0

需要注意的是,即使对象被删除,在这种情况下也不会调用它的析构函数。

这是有道理的,因为构造过程中的异常表明对象从未完全构造(即尚未建立其类不变量),因此调用析构函数可能很危险。

这样做的缺点是,如果构造函数执行了通常由析构函数执行的需要清理的操作,那么现在构造函数有责任在出现异常时执行清理。举个例子:

class C {
private:
    int* p1;
    int* p2;
public:
    C() : p1(new int()), p2(new int()) {}
    ~C() { delete p1; delete p2; }
};

如果分配p2抛出,已经分配的内存p1将泄漏。作为程序员,您有责任以一种不可能发生的方式编写构造函数。

实现这一点的最简单方法是将资源管理职责委托给 RAII 容器类,例如unique_ptr. 这样一来,没有一个类负责管理多个资源,并且不会再出现上述情况。

于 2013-11-12T16:39:01.280 回答
0

你会遇到一个类似的问题,有一个函数 void f(X*, Y*) 并调用 f(new X(), new Y())。如果其中一个新调用成功而另一个失败,则表示内存泄漏。要解决它,您可以创建额外的函数 'X* make_X()' 和 'Y* make_Y()' 返回指针。现在, f(make_X(), make_Y()) 是安全的。(在走了这么远之后,您可能会使用智能指针)

于 2013-11-12T16:39:20.950 回答
0

一切都取决于Y的构造函数。该示例假设构造函数定义明确,并且在抛出之前处理构造函数内部的所有内容。

使用 RAII 和智能指针等std::unique_ptr有助于编写构造函数的安全抛出。例如:

struct Foo
{
    Foo() : a(new A)
    {
        throw std::runtime_error("test");
    }

    std::unique_ptr<A> a;
};

a将被安全清理。

于 2013-11-12T16:42:29.297 回答