构造函数初始化列表中的执行顺序

时间:2010-10-27 20:06:24

标签: c++ operator-precedence

构造函数初始化列表中的执行顺序是否可确定?我知道成员中的成员顺序是这些成员初始化的顺序,但如果我有这样的情景:

class X()
{
X_Implementation* impl_;
};  

and then providing that allocator is available:

X::X():impl_(Allocate(sizeof(X_Implementation)))//HERE I'M ALLOCATING <--1
,impl_(Construct<X_Implementation>(impl_))//AND HERE I'M CONSTRUCTING <--2
{
}

但为了使其可靠,这个顺序必须是从左到右。是否保留了高级书籍std ::或者没有?如果不是,我总是可以将第二条线移动到身体中。

3 个答案:

答案 0 :(得分:31)

根据ISO / IEC 14882:2003(E)第12.6.2节:

  

初始化应按以下顺序进行:

     
      
  • 首先,仅对于如下所述的派生类最多的构造函数,虚拟基类应该   按照它们出现在有向无环图的深度优先从左到右遍历的顺序进行初始化   基类,其中“从左到右”是派生类中基类名称的出现顺序   class base-specifier-list。
  •   
  • 然后,直接基类应按声明顺序初始化,因为它们出现在base-specifier-list中   (无论mem-initializers的顺序如何)。
  •   
  • 然后,非静态数据成员应按其在类定义中声明的顺序进行初始化   (再次无论mem-initializers的顺序如何)。
  •   
  • 最后,执行构造函数的主体。
  •   

所以,按照该顺序,您将获得订单。同样根据标准,订单是这样规定的,这样物体就可以按照完全相反的顺序进行初始化。

答案 1 :(得分:22)

C ++标准确实保证了初始化列表的顺序(ISO C ++标准12.6.2 / 5):

  

......非静态数据成员应该是   按照他们的顺序初始化   在类定义中声明   (再次无论顺序如何   MEM-初始化)。

(有关详细信息,请参阅Wyatt Anderson's answer。)

示例:

class Foo
{
public:
    Foo();
private:
    A a;
    B b;
    C c;
};

Foo::Foo() : b(), a(), c()
{
    // a is initialized first, then b, then c - NOT b, a, then c!
}

但是,你不能两次初始化一个变量 - 你所拥有的不会编译。

class X //() what's with the pair of parentheses you have in your code snippet?
{ 
public:
    X();
private:
    X_Implementation* impl_; 
};   

X::X() : 
    impl_(Allocate(sizeof(X_Implementation))),
    // It is not allowed to initialize a data member twice!
    impl_(Construct<X_Implementation>(impl_))
{ 
} 

相反,只需将额外的工作放入构造函数中:

X::X() : impl_(Allocate(sizeof(X_Implementation)))
{ 
    impl_ = Construct<X_Implementation>(impl_);
}

上述代码可能存在异常安全问题,但不知道Allocate()Construct()实际上是什么,我无法分辨。我可以告诉你,如果你这样做,最好将分配和构造分成他们自己的类,使用资源获取初始化(RAII)成语:

class XBase
{
protected:
    XBase() : impl_(Allocate(sizeof(X_Implementation)))
    {
    }

    ~XBase()
    {
        if(impl_ != 0) { Deallocate(impl_); } // Or something like this
    }

    X_Implementation* impl_; 
};

class X : private XBase // XBase is an implementation detail
{
public:
    X()
    {
        impl_ = Construct<X_Implementation>(impl_);
    }

    ~X()
    {
        Destruct<X_Implementation>(impl_); // Or something like this
    }
};

这样,如果Construct()抛出异常,则不会泄漏内存,因为将调用基类析构函数,这将释放impl_指向的内存。这很重要,因为如果未捕获异常并离开构造函数,则不会调用其匹配的析构函数 。参见Bjarne Stroustrup关于异常安全的论文:http://www2.research.att.com/~bs/except.pdf

答案 2 :(得分:4)

您的具体方案基于多次初始化同一成员的想法。这在C ++中是非法的。您的代码将无法编译。所以,你问的问题并不存在。

成员初始化的顺序是它们在类定义中声明的顺序。在无继承上下文中,它涵盖了与构造初始化列表中的初始化顺序相关的所有内容。