《Effective C++》读书笔记(2)

文章目录

条款4:确定对象被使用前已先被初始化

  • 为内置型对象进行手工初始化,因为C++不保证初始化它们。
  • 构造函数最好使用成员初值列(member initialization list),而不是在构造函数本体内使用赋值操作(assignment)。初值列列出的成员变量,起排序次序应该和它们在class中的声明次序相同。
  • 为了免除“跨编译单元之初始化次序”问题,请以local static对象替换non-local static对象。

内置对象指的是C++自带的类似于int,double的类型。这些类型变量如果没有进行初始化,其值是未定义的,如果对其进行读取会导致不明确的行为(undefined behavior)。所以必须要保证读取内置对象前,其被初始化过。

1
2
int x; 		// bad code
int x = 0; // good code

对于非内置类型,初始化的任务则是由构造函数负责的。包括STL库里的一些类。

1
2
3
std::string str;		//it's ok
std::string str = ""; //it's also ok
Class1 obj;

类成员变量初始化 vs 赋值

类的成员变量在进入构造函数之前已经已经被初始化,所以如果在构造函数内进行”初始化“的的话其实是进行赋值,会带来额外的开销。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Entry {
public:
Entry();
private:
std::string name;
int value;
}

Entry::Entry() {
name = "default"; //
value = 0; // not good
}

Entry::Entry()
: name("default")
, value(0)

至于作者提到的初值列中的顺序需要与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
2
3
4
FileSystem &tfs() {
static FileSystem fs;
return &fs
}

但这个还有一个问题就是static在多线程的情况下会出现不确定性,所以最好在程序启动时先以单线程的方式调用一遍。

总之,non-local static对象最好只在一个编译单元内使用,如果需要跨编译单元使用,则使用单的方法替代。