深度探索C++对象模型<六>

Member Initialization List

在使用构造函数的时候,你可以用两种方式来设定成员变量的初始值。要么使用initialization list,要么在构造函数内部进行构造。

首先说明,在以下的情形必须使用初始化列表:

  • 初始化一个引用变量
  • 初始化一个常量
  • 调用基类的constructor,并且它拥有一组参数
  • 调用成员类对象的constructor,并且它拥有一组参数

如果是在构造函数体内进行初始化,如下:

1
2
3
4
5
6
7
8
9
class Word {
string _name;
int _cnt;
public:
Word() {
_name = 0;
_cnt = 0;
}
}

它会被编译后修改为:

1
2
3
4
5
6
7
Word::Word() {
_name.string::string();
string tmp = string(0);
_name.string::operator(temp);
temp.string::~string();
_cnt = 0;
}

也就是会定义了一个临时值,降低了效率。

然而如果是用的初始化列表:

1
2
3
4
5
6
7
8
9
10
11
Word::Word: _name(0){
_cnt = 0;
}

//会被扩张成

Word::Word()
{
_name.string::string(0);
_cnt = 0;
}

编译器的操作

编译器会按照声明顺序一一地执行initialization list,在constructor安插初始化的操作。

注意是按照声明的顺序,例如以下的情况会出问题:

1
2
3
4
5
6
class X {
int i;
int j;
public:
X(int val):j(val), i(j){...}
}

由于i先初始化,但j还没初始化,这样就会出现无法预知的值。

因此,可以这样设置:

1
2
3
X::X(int val):j(val){
i = j;
}

因为initialization list的初始化操作,编译器会将初始化操作安插在explicit user assignment的操作之前。

调用member function设置初始值

考虑这样的例子:

1
X::X(int val): i(xfoo(val)), j(val){}

在这种情况下不会有问题,因为this指针已经构建号,代码会被扩张成:

1
2
3
4
X::X(int val){
i = this->xfoo(val);
j = i;
}