《Effective C++》读书笔记(2)
条款4:确定对象被使用前已先被初始化
- 为内置型对象进行手工初始化,因为C++不保证初始化它们。
- 构造函数最好使用成员初值列(member initialization list),而不是在构造函数本体内使用赋值操作(assignment)。初值列列出的成员变量,起排序次序应该和它们在class中的声明次序相同。
- 为了免除“跨编译单元之初始化次序”问题,请以local static对象替换non-local static对象。
内置对象指的是C++自带的类似于int,double的类型。这些类型变量如果没有进行初始化,其值是未定义的,如果对其进行读取会导致不明确的行为(undefined behavior)。所以必须要保证读取内置对象前,其被初始化过。
1 | int x; // bad code |
对于非内置类型,初始化的任务则是由构造函数负责的。包括STL库里的一些类。
1 | std::string str; //it's ok |
类成员变量初始化 vs 赋值
类的成员变量在进入构造函数之前已经已经被初始化,所以如果在构造函数内进行”初始化“的的话其实是进行赋值,会带来额外的开销。
1 | class Entry { |
至于作者提到的初值列中的顺序需要与class内变量声明的顺序保持一致是因为c++总是按照class内变量声明的顺序对变量,所以为了避免带来迷惑以及容易检查是否漏掉变量未初始化,最好将初值列顺序与class内变量声明顺序保持一致。
non-local static 对象的初始化
不同编译单元内定义的non-local static对象初始化次序未定义。
编译单元
编译单元是指产出单一目标文件(object file)的那些源码,基本上是它的源文件加上其所含入的头文件。
non-local static 对象
除了定义在函数内的static变量,其他的static变量均是non-local static对象,包括global对象、定义域namespace作用域内的对象、class内的、文件作用域内的static对象。
次序未定义会导致一个问题,就是如果你需要在一个编译单元内使用另一个编译单元的静态变量,则很有可能你在调用的时候其还没有初始化。这种情况你可以通过使用返回一个local static变量的reference的方法来得到,这个方法就是单例的常见使用场景。
1 | FileSystem &tfs() { |
但这个还有一个问题就是static在多线程的情况下会出现不确定性,所以最好在程序启动时先以单线程的方式调用一遍。
总之,non-local static对象最好只在一个编译单元内使用,如果需要跨编译单元使用,则使用单的方法替代。