使用委托构造函数来避免泄漏

时间:2014-10-06 11:00:49

标签: c++ c++11 memory-leaks

在演讲中,以下代码已被证明是不安全的,因为如果构造函数抛出,则不会调用析构函数并泄漏资源:

class TwoResources {
    TwoResources(int x, int y)
        : m_a(nullptr), m_b(nullptr) {
        m_a = new A(x); m_b = new B(y);
    }
    ~TwoResources() {
        delete m_b; delete m_a;
    }
    A * m_a; B * m_b;
};

建议解决方案是使用委托构造函数,如下所示:

class TwoResources {
    TwoResources() : m_a(nullptr), m_b(nullptr) { }
    TwoResources(int x, int y) : TwoResources() {
        m_a = new A(x); m_b = new B(y);
    }
    ~TwoResources() {
        delete m_b; delete m_a;
    }
    A * m_a; B * m_b;
};

这是安全的,因为:

  

C ++ 11 15.2 [except.ctor] / 2:“如果是非委托构造函数的话   对象已完成执行,并为此委托构造函数   对象存在异常,对象的析构函数将是   调用“。

然而,在同一张幻灯片中,它说:

  

仅仅因为你可以利用这条规则并不意味着你应该这样做!

如果确保此代码安全,那么它有哪些潜在问题?

2 个答案:

答案 0 :(得分:6)

仅仅因为某些东西是安全的并不意味着这样做是个好主意。

例如,使用该委托构造函数调用对于异常安全至关重要,但对于不熟悉该语言错综复杂的代码的随意读者而言,这一点并不明确。一个月后,看到你的代码的其他人可能会想“如果你再次在构造函数体中设置它,为什么要将它设置为null?”并删除它。哎哟。

此外,当您手动管理生命周期时,您需要编写自己的复制/移动构造函数/赋值运算符。小姐和破坏结果。如果使用unique_ptr来管理生命周期,那么编译器生成的移动构造函数/赋值运算符和析构函数将执行正确的操作,如果您尝试复制而不自行实现复制构造函数,它会抱怨。

答案 1 :(得分:1)

这一点当然不清楚。有人可以通过仅在一个构造函数中添加新变量来到达并修改代码,而无需了解第二个构造函数的有用性。 您应该考虑使用std::unique_ptr

class TwoResources 
{
    TwoResources(int x, int y) : m_a( new A( x ) ), m_b( new B( y ) )
    {
    }
    std::unique_ptr<A> m_a;
    std::unique_ptr<B> m_b;
};

另外,看看std::make_unique比使用new和delete运算符手动更安全:

class TwoResources 
{
    TwoResources(int x, int y):m_a( std::make_unique<A>( x ) ), m_b( std::make_unique<B>(B( y ) )
    {
    }
    std::unique_ptr<A> m_a;
    std::unique_ptr<B> m_b;
};