C++ 标准确实保证了初始化列表的顺序(ISO C++ 标准 12.6.2/5):
...非静态数据成员应按照它们在类定义中声明的顺序进行初始化(同样与 mem-initializers 的顺序无关)。
(有关详细信息,请参阅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() 究竟是什么的情况下,我无法判断。我可以告诉你,如果你这样做的话,最好将分配和构造分离到它们自己的类中,使用 Resource Acquisition Is Initialization (RAII) 习惯用法:
class XBase
{
protected:
XBase() : impl_(Allocate(sizeof(X_Implementation))) { }
~XBase() {
if( !impl_) { 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's paper on exception safety。