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

The*_* do 10 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
{
}
Run Code Online (Sandbox Code Playgroud)

但为了使这个可靠,这个顺序必须是从左到右.它是由GREAT BOOK OF std保证还是不保证?如果不是,我总是可以将第二条线移动到身体中.

Wya*_*son 31

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

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

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

因此,按照该顺序,您将获得订单.同样根据标准,订单被如此规定,使得对象可以以完全相反的顺序未初始化.


In *_*ico 22

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

...非静态数据成员应按其在类定义中声明的顺序进行初始化(同样不管mem-initializers的顺序如何).

(有关更多信息,请参阅Wyatt Anderson的答案.)

例:

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!
}
Run Code Online (Sandbox Code Playgroud)

但是,您无法初始化变量两次 - 您将无法编译.

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_))
{ 
} 
Run Code Online (Sandbox Code Playgroud)

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

X::X() : impl_(Allocate(sizeof(X_Implementation)))
{ 
    impl_ = Construct<X_Implementation>(impl_);
}
Run Code Online (Sandbox Code Playgroud)

上述代码可能存在异常安全问题,但不知道我无法分辨出什么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
    }
};
Run Code Online (Sandbox Code Playgroud)

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