首页 > 代码库 > 初始化列表的使用

初始化列表的使用

下列情况中, 为了让程序顺利编译, 必须使用 member initialization list:
1. 初始化一个 reference member 时;
2. 初始化一个 const member 时;
3. 当调用一个 base class 的 constructor, 而它拥有一组参数时;
4. 当调用一个 memeber class 的 constructor, 而它拥有一组参数时.

考察以下代码:

class Word{    String _name;    int    _cnt;public:    //没有错误, 只是效率低    Word()    {        _name = 0;            _cnt  = 0;    }};

这种情况下, Word constructor 会先产生一个暂时性的 String object, 然后将它初始化, 再以一个 assignment 运算符将暂时性的 object 指定给 _name, 然后再摧毁这个暂时性的 object, 以下是constructor 可能的内部扩张结果:

//可能的结果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(): _name(0){        _cnt = 0;}//它会被扩张成这个样子//可能的代码Word::Word(/* this pointer goes here */){        //调用 String(int) constructor        _name.String::String(0);        _cnt = 0;}

这又引起另外一个问题, 是否每个 member 都必须使用 member initialization list 来初始化呢?

initialization list 不是一组函数, 编译器对于 initialization list , 会以适当次序在 constructor 之内安插初始化操作, 并且在任何 explicit user code 之前(例如之前 class Word 的初始化) 重点在于, 初始化的顺序是由 class 中的 members 声明的顺序决定, 不是由 initialization list 中的排列次序决定, 这就可能导致以下的问题:

class X{        int i;        int j;public:        //buggy, i(j) 会产生不确定行为        X(int val):j(val), i(j)        {}  };//较好的处理方式X::X(int val):j(val){ i = j; }

还有一个有趣的问题, initialization list 中的项目被安插到 constructor 的函数体中, 会继续保存声明次序吗? 也就是说, 对于前一个代码, j 的初始化操作会安插在 explicit user assignment 操作 ( i = j ) 之前还是之后? 如果继续保存, 则这样操作也会导致不确定行为. 然而以上的代码是正确的, 因为 initialization list 的项目被放在 explicit user code 之前.

另一个常见的问题是, 能否像下面那样, 调用一个 member function 以设定一个 member 的初值:

//X::XFoo() 被调用X::X(int val): i(XFoo(val), j(val)){}

答案是肯定的, 但是, 值得注意: 请使用 存在于 constructor 函数体内的一个 member, 而不要使用 存在于 initialization list 中的 member 设定初值. 因为你并不知道 XFoo() 对于 X object 的依赖程度有多高, 如果把 XFoo() 放在 constructor 体内, 那么对于到底是哪一个 member 在 XFoo() 执行时被设立初值这件事, 就不会导致模棱两可的情况. 

用 member function 来初始化是合法的, 它会被编译器扩充为:

// constructor 被扩充的结果X::X(/* this pointer */ int val){        i = this->XFoo(val);        j = i;}

那么如果一个 derived class member function 被调用, 其返回值被当作 base class constructor 的一个参数, 将会如何:

class FooBar: public X{    int _fval;public:    int fval(){return _fval;}    FooBar(int val): _fval(val), X(fval())    //使 fval() 作为 base class constructor 的参数    {}    ...};//这是个好主意吗?//这是可能的扩张结果FooBar::Foobar(/* this pointer goes here*/){    //编译器会把 initialization list 的代码扩展到 user code 的 前面    X::X(this, this->fval());    //wtf, 导致不明确行为    _fval = val;}

 

初始化列表的使用