1. “将对象初始化”这事,C++似乎反复无常。
在某些语境下x保证被初始化(为0),p的成员变量有时候被初始化(为0),有时候不会。
读取未初始化的值会导致不明确的行为。
2. 永远在使用对象之前先将它初始化
对内置类型,手动初始化。
至于内置类型以外的任何其他东西,初始化责任落在构造函数(constructors)身上。规则很简单:确保每一个构造函数都将对象的每一个成员初始化。
这个规则很容易奉行,重要的是别混淆了赋值(assignment )和初始化( initialization )。
C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。
构造函数的一个较佳写法是,使用所谓的member initialization list(成员初值列)替换赋值动作:
好的习惯:总是在初值列中列出所有成员变量(如果成员变量是const或references,它们就一定需要初值,不能被赋值)。
classes拥有多个构造函数,每个构造函数有自己的成员初值列。这种情况下可以合理地在初值列中遗漏那些“赋值表现像初始化一样好”的成员变量,改用它们的赋值操作,并将那些赋值操作移往某个函数(通常是private,供所有构造函数调用。
3. 成员初始化次序
次序总是相同:base classes更早于其derived classes被初始化,而class的成员变量总是以其声明次序被初始化。
当你在成员初值列中条列各个成员时,最好总是以其声明次序为次序。
4. static对象
所谓static对象,其寿命从被构造出来直到程序结束为止,因此stack和heap-based对象都被排除。这种对象包括global对象、定义于namespace作用域内的对象、在classes内、在函数内、以及在file作用域内被声明为static的对象。
函数内的static对象称为local static对象(因为它们对函数而言是local),其他static对象称为non-local static对象。程序结束时static对象会被自动销毁,也就是它们的析构函数会在main ( )结束时被自动调用。
5. non-local static对象的初始化次序。
C++对“定义于不同编译单元内的non-local static对象的初始化次序并无明确定义。
将每个non-local static对象搬到自己的专属函数内(该对象在此函数内被声明为static)。这些函数返回一个reference指向它所含的对象。换句话说,non-local static对象被local static对象替换了。
这是Singleton模式的一个常见实现手法。
这个手法的基础在于:C++保证,函数内的local static对象会在“该函数被调用期间”“首次遇上该对象之定义式”时被初始化。所以如果你以“函数调用”(返回一个reference指向local static对象)替换“直接访问non-local static对象”。
此种方式适合inline(频繁调用,短)
但是从另一个角度看,这些函数“内含static对象”的事实使它们在多线程系统中带有不确定性。再说一次,任何一种non-const static对象,不论它是local或non-local,在多线程环境下“等待某事发生”都会有麻烦。处理这个麻烦的一种做法是:在程序的单线程启动阶段( single-threaded startup portion)手工调用所有reference-returning函数,这可消除与初始化有关的,’竞速形势(race conditions )。
6. 总结
1)为内置型对象进行手工初始化,因为c什不保证初始化它们。
2)构造函数最好使用成员初值列(member initialization list ),而不要在构造函数本体内使用赋值操作(assignment )。初值列列出的成员变量,其排列次序应该和它们在class中的声明次序相同。
3)为免除“跨编译单元之初始化次序”问题,请以local static对象替换non-local static对象。