如何正确初始化对象.[C++]

The*_* do 5 c++ initialization

我在之前的一个问题中提到过,我正在阅读Herb Sutter和Andrei Alexandrescu撰写的"C++编码标准"一书.在其中一章中他们说的是这样的:

始终在构造函数体中而不是在初始化列表中执行非托管资源获取,例如其结果未立即传递给智能指针构造函数的新表达式.

这是否意味着我应该使用这种形式的构造(假设data_3_必须用new初始化):

SomeClass(const T& value, const U& value2, const R& value3)
    : data_(value), data_2_(value2)
{
    data_3_ = new value3;
}
Run Code Online (Sandbox Code Playgroud)

代替:

SomeClass(const T& value, const U& value2, const R& value3)
    : data_(value), data_2_(value2), data_3_(new value3)
    // here data_3_ is initialized in ctor initialization list
    // as far as I understand that incorrect way according to authors
{
}  
Run Code Online (Sandbox Code Playgroud)

提前致谢.

PS如果这就是他们的意思,为什么他们使用术语非托管资源获取?我一直认为这些资源是"手动管理"的?

PS 2.我很抱歉,如果这篇文章中有任何格式问题 - 我不得不承认 - 我绝对讨厌在这个论坛上格式化的方式.

Mik*_*our 10

如果类包含两个或更多非托管资源,则必须提供建议.如果一个分配失败,那么您将需要释放所有先前分配的资源以避免泄漏.(编辑:更一般地说,分配资源后抛出的任何异常都必须通过删除该资源来处理).如果在初始化列表中分配它们,则无法执行此操作.例如:

SomeClass() : data1(new value1), data2(new value2) {}
Run Code Online (Sandbox Code Playgroud)

value1如果new value2抛出会泄漏.您将需要处理此问题,如下所示:

SomeClass() : data1(0), data2(0)
{
    data1 = new value1; // could be in the initialiser list if you want
    try
    {
        data2 = new value2;
    }
    catch (...)
    {
        delete data1;
        throw;
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,通过合理使用智能指针可以避免所有这些恶作剧.


Ale*_*x B 9

如果构造函数在任何阶段抛出异常,则手动管理资源的初始化可能会导致资源泄漏.

首先,考虑使用自动管理资源的此代码:

class Breakfast {
public:
    Breakfast()
        : spam(new Spam)
        , sausage(new Sausage)
        , eggs(new Eggs)
    {}

    ~Breakfast() {}
private:
    // Automatically managed resources.
    boost::shared_ptr<Spam> spam;
    boost::shared_ptr<Sausage> sausage;
    boost::shared_ptr<Eggs> eggs;
};
Run Code Online (Sandbox Code Playgroud)

如果"new Eggs"抛出,~Breakfast则不会被调用,但是所有构造成员的析构函数都以相反的顺序调用,这是sausage和的析构函数spam.

所有资源都已正确发布,这里没问题.

如果您使用原始指针(手动管理):

class Breakfast {
public:
    Breakfast()
        : spam(new Spam)
        , sausage(new Sausage)
        , eggs(new Eggs)
    {}

    ~Breakfast() {
        delete eggs;
        delete sausage;
        delete spam;
    }
private:
    // Manually managed resources.
    Spam *spam;
    Sausage *sausage;
    Eggs *eggs;
};
Run Code Online (Sandbox Code Playgroud)

如果"new Eggs"抛出,记住,~Breakfast不叫,但相当的析构函数spamsausage(这是什么在这个事业,因为我们有原始指针为实际的对象).

因此你有泄漏.

重写上面代码的正确方法是:

class Breakfast {
public:
    Breakfast()
        : spam(NULL)
        , sausage(NULL)
        , eggs(NULL)
    {
        try {
            spam = new Spam;
            sausage = new Sausage;
            eggs = new Eggs;
        } catch (...) {
            Cleanup();
            throw;
        }
    }

    ~Breakfast() {
        Cleanup();
    }
private:
    void Cleanup() {
        // OK to delete NULL pointers.
        delete eggs;
        delete sausage;
        delete spam;
    }

    // Manually managed resources.
    Spam *spam;
    Sausage *sausage;
    Eggs *eggs;
};
Run Code Online (Sandbox Code Playgroud)

当然,您应该更喜欢将每个非托管资源包装在一个单独的RAII类中,这样您就可以自动管理它们并将它们组合到其他类中.


Kea*_*eks 3

这是因为 SomeClass 的构造函数可能会抛出异常。

在您描述的情况下(即不使用智能指针),您必须释放析构函数中的资源,并且如果 SomeClass 的构造函数抛出异常,则使用 try-catch 块:

SomeClass(const T& value, const U& value2, const R& value3):data_(value),data_2_(value2) :
data_3_(NULL)
{
    try 
    {
        data_3_ = new value3;

        // more code here that may throw an exception
    }
    catch(...)
    {
        delete data_3_;
        throw;
    }
}
Run Code Online (Sandbox Code Playgroud)

.. 如果初始化列表中抛出异常,则无法执行此操作。

请参阅以获得进一步的解释。

  • 抱歉,这不是真的。如果 value3 的构造函数抛出异常,则 new 分配的内存将被释放 - 请参阅 C++ DStandard 中的 5.3.4/17。即使是这样,new也不会返回,data_3_也不会被分配,这意味着它不能被删除。 (10认同)
  • Neil 是对的:阅读 5.3.4/17 的 C++ 标准:http://www.google.com.au/url?sa=t&amp;source=web&amp;ct=res&amp;cd=2&amp;ved=0CA8QFjAB&amp;url=http%3A%2F%2Fwww.open -std.org%2Fjtc1%2Fsc22%2Fwg21%2Fdocs%2Fpapers%2F2005%2Fn1905.pdf&amp;ei=BvEsS_rEK82IkAWF1-SBCQ&amp;usg=AFQjCNEFrVBugtLPNpfMN_1YZ4VszwnHiA&amp;sig2=wAC00WyDMjng2k1cO52 9号周 (4认同)
  • @Gregory唯一可能抛出的其他东西是新的,在这种情况下你仍然无法删除任何东西。 (3认同)
  • @atch:这个答案描述了如果对象自己的分配失败则删除对象,这是错误的。我的回答描述了如果构造函数稍后出现故障,则删除已分配的对象。 (3认同)