核心思想:对象使用前必须初始化。
C++对象初始化没有简明的规则。大致而已,出于效率考虑,C++的C部分(参见条款1)不保证进行初始化,而非C的部分能保证初始化。读取未初始化的随机值,将导致未定义的行为,包括程序异常退出。
最好的做法是:在使用前永远进行初始化,对内置数据类型进行手工初始化。
对于对象而已,初始化的职责落在构造函数上。成员变量初始化的好的做法是使用初始化列表,而不是在构造函数的函数体中赋值。两者之间至少有以下区别:
- 初始化列表在构造函数体之前执行
- 如果成员对象有缺省构造函数,又没有写在初始化列表中,C++编译器函数对它进行确省构造。故如果采用初始化列表是调用一次构造,而使用函数体中赋值则调用了一次缺省构造和一次赋值,相对效率较低。
初始化列表需注意以下几点:
- 初始化的顺序不是按初始化列表中出现的顺序,而是成员变量声明的顺序
- 基类在派生类前被初始化,以基类声明的顺序
- 如果存在virtual基类的话,virtual基类在普通基类之前被初始化,顺序是从最深到最浅,从左到右[1]
- 以下四中情况必须使用初始化列表: 1) const成员变量, 2)reference成员变量, 3)成员对象的构造函数有参数, 4)基类的构造函数有参数。
- 如果初始化成员变量时调用了成员函数,建议不要在初始化列表中这样做,防止该成员函数中对其他成员变量的依赖问题。[2]。
最后,Mayers谈了不同编译单元(指.cpp文件)中非局部(non-local)静态变量的初始化问题。结论是:其初始化顺序是不定的。有两种解决方案:
1) 将static变量定义在一个函数内部。局部static变量在第一次使用到时定义,其顺序是固定的
2) 使用Singleton设计模式。
但是在多线程环境下仍存在问题,方案1的一种处理方法是在程序启动的单线程环境中访问生成各static对象。Singleton模式的解决方案是采用double-checked locking optimiazation模式[3]。
参考文档:
[1] 参见《C++对象模型》5.2节
[2] 参见《C++对象模型》2.4节
[3] 参见 《面向模式的软件体系结构》之二 4.4节。