effective C++ 条款 4:确定对象被使用前已先被初始化
对象的成员变量的初始化动作发生在进入构造函数本体之前。
ABEntry::ABEntry(const std::string& name, const std::list<PhoneNumber>& phones)
{
theName = name; //这些都是赋值,不是初始化
thePhones = phones;
numTimesConsulted = 0;
}
这个构造函数首先调用default构造函数为theName, thePhones设初值,然后立刻对他们赋予新值
ABEntry::ABEntry(const std::string& name, const std::list<PhoneNumber>& phones)
:theName(name), // 这些是初始化
thePhone(phones),
numTimesConsulted(0)
{}
本例中的构造函数分别以name等为初值进行copy构造。
class的成员变量总是以其声明的次序被初始化。跟初始化列表中的顺序无关
编译单元是指产出单一目标文件的那些源码。基本上是单一源码文件加上其所含的头文件。
static对象,从被构造出来到程序结束为止,析构函数会在main()结束时被自动调用。
函数内的static对象称为local static对象;其他的包括global对象,定义于namespace作用域内的对象,在class内、以及在file作用域内被声明为static的对象被称为non-local static对象。
c++对“定义于不同编译单元内的non-local static对象”的初始化次序并无明确定义。
class FileSystem{ //来自你的程序库
public:
…
std::size_t numDisk() const;
…
};
extern FileSystem tfs; //预备给客户用的对象
class Directory{ //由程序库客户建立
public:
Directory(params);
…
};
Directory::Directory(params)
{
…
std::size_t disks = tfs.unmDisks(); //使用tfs对象
…
}
Directory tempDir(params)
由于tempDir和tfs属于定义在不同编译单元内的non-local static对象,不能保证tfs在tempDir之前被初始化。
消除这个问题的方法是将每个non-local static对象搬到自己的专属函数内,换成local static对象,做法有点像Sigleton模式。
c++保证函数内的local static 对象会在该函数被调用期间,首次遇上该对象的定义式时被初始化。所以以函数调用(返回一个reference指向local static对象)来替换直接访问non-local static对象。这样保证获得一个经过初始化的对象。并且,如果从未调用过non-local static对象的“仿真函数”,就绝不会引发构造函数和析构函数成本; 真正的non-local static对象可没这么便宜。
//reference returning 函数防止初始化次序问题
class FileSystem{…};
FileSystem& tfs() //这个函数用来替换tfs对象
{
static FileSystem fs;
return fs;
}
class Directory{…};
Directory::Driectory(params)
{
…
std::size_t disks = tfs().numDisks();
…
}
Directory& tempDir() //这个函数用来替换tempDir对象
{
static Directory td;
return td;
}
任何non-const static对象不论是local或non-local,在多线程环境下“等待某事发生”都会有麻烦。处理这个麻烦的一种做法是:在程序的单线程启动阶段手工调用所有reference-returning函数,这可消除与初始化有关的“竞速形势”(race condition)。