处理失败的构造函数

时间:2013-11-12 16:18:17

标签: c++ exception constructor

我正在阅读来自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 个答案:

答案 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将被安全清理。