构造函数初始化列表中的执行顺序
构造函数初始化列表中的执行顺序是否可确定? 我知道,在一个class级的成员顺序是这些成员将被初始化的顺序,但如果我有这样的情景:
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 ::的伟大的书保证? 如果不是,我总是可以将第二行移入正文。
根据ISO / IEC 14882:2003(E)第12.6.2节:
初始化应按以下顺序进行:
- 首先,对于下面描述的派生类最多的构造函数,虚拟基类应按照它们出现在基类的有向无环图的深度优先从左到右遍历的顺序进行初始化,其中“左从右到左“是派生类base-specifier-list中基类名的出现顺序。
- 然后,直接基类应按声明顺序进行初始化,因为它们出现在base-specifier-list中(不pipemem-initializer的顺序如何)。
- 然后,非静态数据成员应按照在类定义中声明的顺序进行初始化(不pipemem初始化的顺序如何)。
- 最后,构造函数的主体被执行。
所以,按照这个顺序,你会得到你的订单。 同样根据该标准,顺序被规定为使得对象可以以相反的顺序未初始化。
C ++标准确保初始化列表(ISO C ++ Standard 12.6.2 / 5)的顺序:
…非静态数据成员应按照它们在类定义中声明的顺序进行初始化(不pipemem-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! }
但是,你不能初始化一个variables两次 – 你有什么将不会编译。
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_); }
上面的代码可能会有exception的安全问题,但是不知道我Allocate()
或Construct()
实际上我无法知道。 我可以告诉你,如果你使用资源获取初始化(RAII)习惯用法,最好是将分配和构build分离到他们自己的类中:
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()
抛出一个exception,你将不会泄漏内存,因为基类析构函数将被调用,这将释放impl_
指向的内存。 这很重要,因为如果exception没有被捕获并且离开构造函数,那么它的匹配的析构函数将不会被调用 。 请参阅Bjarne Stroustrup关于exception安全性的论文: http : //www2.research.att.com/~bs/except.pdf
您的具体情况是基于不止一次初始化同一个成员的想法。 这在C ++中是非法的。 你的代码不会被编译。 所以,你所问的问题并不存在。
成员初始化的顺序是它们在类定义中声明的顺序。 在无inheritance上下文中,涵盖了与构造初始值设定项列表中初始化顺序相关的所有内容。