-
为内置类型对象手工初始化,因为C++不保证初始化他们
-
构造函数最好使用构造函数初始值列表,而不要在构造函数体内使用赋值操作,初值列列出的成员变量其次序应该与类中声明次序相同
-
为了免除‘’跨编译单元之初始化次序‘’问题,请以local static对象替换non-local static对象
读取未初始化值会导致不明确行为:
int x;
在不同位置会出现不同行为,对于内置类型来说,如果在全局作用域声明,则默认初始化为0(对于int),但是如果是在局部作用域,其值是不确定的,未初始化的。对于类对象来说,其行为与其默认构造函数相关,但总的来说,最好在使用对象之前先把他初始化,确保每一个构造函数将对象的每一个成员变量都初始化
剩下重要的是区分赋值与初始化:
举例:考虑一个用来表示通讯录的类
.h文件中
class PhoneNuber{...};
//
class ABEntry{ //ABEntry="address book entry"
public:
ABEntry(const std::string&name,const std::string&address,const std::list<PhoneNuber>&phones);
private:
std::string theName;
std::string theAddress;
std::list<PhoneNuber> thePhones;
int numTimesConsulted;
}
.cpp文件中:
ABEntry::ABEntry(const std::string&name,const std::string&address,const std::list<PhoneNuber>&phones)
{
theName=name; //这些都是赋值
theAddress=address; //而不是初始化
thePhones=phones;
numTimesConsulted=o;
}
以上列子是会导致赋值的,赋值的成本是先调用default构造函数为成员变量设初值,然后再立刻赋予新值;C++规定是,对象的成员变量初始化动作发生在进入构造函数体之前;
再看member initialization list(成员列表初始值):
ABEntry::ABEntry(const std::string&name,const std::string&address,const std::list<PhoneNuber>&phones)
:theName(name), //这些都是初始化了
theAddress(address),
thePhones(phones),
numTimesConsulted(0)
{} // 构造函数本体不需要有任何动作
这个列表初始化与前面赋值那个结果相同,但是效率更高,不需要调用default构造函数了,直接利用各个实参为成员变量调用一次copy构造设定初值;
现在这一条款中就剩下不同编译单元内定义的non-local static对象的初始化次序问题了。
所谓编译单元,是指产出单一目标文件的那些源码,基本上是单一源码文件加上其所含入的头文件(#include files)。
当对于多个编译单元,并且每一个都至少包含一个non-local static 对象时(也就是位于global或者namespace作用域内 ,亦或在class内这file作用域内声明为static),真正的问题是,当一个编译单元内的某个non-local static对象的初始化使用了另一个编译单元内的某个non-local static对象,而他用到的这个对象可能还没有被初始化;这是因为C++对“定义于不同编译单元的non-local static对象”的初始化顺序并没有明确定义(给coder增加负担。。。)列子见effecttive C++ P30;
解决方法是Singleton模式的一种常见实现手法(首先说明此方法可行的原因是:C++保证,函数内的local static对象会在该函数被调用期间,首次遇上该对象定义式时被初始化;所以如果用函数调用返回一个reference指向local static对象,替换掉直接访问non-local static)
//Singl
class FileSystem{...};
FileSystem & instance()
{
static FileSystem fs;
return fs;
}