必须使用member initialization list的四种情况:
- 当初始化一个reference member时;
- 当初始化一个const member时;
- 当调用一个base class的constructor,而它拥有一组参数时;
- 当调用一个member class的constructor,而它拥有一组参数时。
class Word {
string _name;
int _cnt;
public:
Word() {
_name = 0;
_cnt = 0;
}
}
Word constructor会先产生一个临时性的string object,然后将它初始化,之后以一个assignment运算符将临时性object指定给_name,随后再摧毁那个临时性object。
// C++伪码
Word::Word(/* this pointer goes here*/)
{
// 调用string的default constructor
_name.string::string();
// 产生临时性对象
string temp = string(0);
// "memberwise"地拷贝_name
_name.string::operator=(temp);
// 摧毁临时性对象
temp.string::~string();
_cnt = 0;
}
修正后:
// 较佳的方式
Word::Word():string(0)
{
_cnt = 0;
}
扩张的伪码:编译器会一一操作initialization list,以适当顺序在constructor之内安插初始化操作,并且在任何explicit user code之前。
// C++伪码
Word::Word(/* this pointer goes here*/)
{
// 调用string constructor
_name.string::string(0);
_cnt = 0;
}
list中的项目顺序是由class中的members声明顺序决定的,不,会是由initialization list中的排列顺序决定的。
members声明顺序和initialization list中的排列顺序之间的外观错误会导致下面意想不到的危险。
class X {
int i;
int j;
public:
X(val) :j(val), i(j)
{
}
}
代码看起来是把j设初值val,再把i设初值j。问题在于,由于声明顺序的缘故,initialization list中的i(j)其实比j(val)更早执行。但因j一开始未有初值,所以i(j)的执行结果导致i无法预知其值。修改为:
class X {
int i;
int j;
public:
X(val) :j(val)
{
i = j;
}
}
另一个常见的问题是,调用一个member function以设定一个member初值:
X::X(int val) : i(xfoo(val)), j(val) // xfoo(val)是X的一个member function
{
}
请使用“存在于constructor体内的一个member”,而不要使用“存在于member initialization list的member”,来为另一个member设定初值。你并不知道xfoo()放在constructor体内,那么对于“到底是哪一个member在xfoo()执行时被设立初值”这件事,就可以确保不会发生模棱两可的情况。
member的使用是合法的,这是因为和此object相关的this指针已经被构建妥当,而constructor大约被扩充为:
// c++伪码
X::X(/*this pointer*/int val) // xfoo(val)是X的一个member function
{
i = this->X::xfoo(val);
j = val;
}
最后,如果一个derived class member function被调用,其返回值被当做base class constructor的一个参数,将会如何:
// 调用FooBar::fval()可以吗?
class Foobar : public X {
int _fval;
public:
int fval() { return _fval;}
FooBar(int val):_fval(val), X(fval())
{
}
};
扩张结果:
FooBar(/*this pointer goes here*/)
{
// 实在不是一个好主意
X::X(this, this->fval());
_fval = val;
}
简略地说,编译器会对initialization list 一一处理并可能重新排序,以反映出members的声明顺序。它会安插一些代码到constructor体内,并置于任何explicit user code之前。