构造函数语义学 The Semantics of Constructors
这一期,我们讲 class 的 成员初值化列表(Member Initialization List)的使用以及可能存在的坑
使用 Member Initialization List 情况
- 当初始化一个 reference member 时
- 当初始化一个 const member 时
- 当初始化一个 base class 的 constructor 时,而它拥有一组参数‘
- 当初始化一个 member class 的 constructor 时,而它拥有一组参数
但是在上述的情况下,使用初值化列表能够明显地提高效率
class Word {
public:
Word()
{
_name = 0;
_cnt = 0;
}
private:
String _name;
int _cnt;
};
// 编译器进行扩张后的结果
Word::Word()
{
_name.Sring::String();
String temp = String(0);
_name.String::operator=(temp);
temp.String::~String();
_cnt = 0;
}
// 通过使用初值化列表提升效率
Word::Word()
: _name(0)
{
_cnt = 0;
}
// 扩张之后
Word::Word()
{
_name.String::String(0);
_cnt = 0;
}
使用初值化列表可能存在的陷阱
-
初值化列表的初始化顺序
初值化列表对 member 的初始化顺序是成员在 class 中声明的顺序。
下面的使用就会存在问题
class Test { public: Test : t2(0), t1(t2) {} private: int t1, t2; }
不难看出,使用者是想通过初值化列表先赋值给 t2,再将 t2 的值赋给 t1
但是初值化列表的初始化的顺序是 先 t1, 再 t2。哈哈
-
在初值化列表中调用一个 member functio 的值来设定另一个 member 的值。
作者在书中给出的忠告:**请使用 “存在于 constructor 体内的一个 member”,而不要使用 “存在于 member initialization list 中的 member”,来为另一个 member 设定初值。**原因是你并不知道这个 member function 对 class object 本身的依赖性有多高,如果把 member function 放在 constructor 体内,那么对于 “到底是哪一个 member 在 member function 执行时被设立初值” 这件事,就可以确保不发生摸棱两可的情况。
X::X( int val ) : i( xfoo(val)), j(val) {} //扩充后 X::X(int val) { i = this->xfoo(val); j = val; }
-
一个 derived class member function 被调用,其返回值被当作 base class constructor 的一个参数
作者不推荐
class FooBar : public X { public: int fval() { return _fval; } FooBar( int val ) : _fval( val ), X(fval) { } private: };
本章对初值化列表的学习就到这里,后面还会在其他章节深入地讲解一下初值化列表,顺便分析他的底层的实现。