C++规定对象的初始化动作发生在进入构造函数主体之前。(初始化列表比在构造函数体内初始化的效率高,以为在函数体内初始化会调用默认的构造函数)
编译单元是指产出单一目标文件的那些源码。
问题:
两个源码文件,每个文件中至少有一个non-local static对象(即该对象是全局的或者位于namespace作用域内,或在class内或file的作用域内被声明为static)。如果某编译单元内的non-local static对象的初始化用动作使用了另一个单元内的某个non-local static对象,在这个时候由于c++对定义在不同的编译单元内的non-local static对象的初始化次序没有明确的规定,所以用可能引发错误。
例子:
你写的程序(一个编译单元):
class FileSystem { public: … size-tnumDisk() const; … };
Extern FileSystem tfs;// 准备给别人用的对象
别人的程序(另一个编译单元)
class Directory { Directory(params) { Size_tdisk=tfs.numDisks(); //使用第一个编译单元的对象,假设在编译这个文件之//前已经编译了上一个文件那么没错,如果相反呢? } };
解决办法:
将每个non-local static对象搬到自己专属的函数内(也就是在函数内声明为static)。这些函数返回一个引用指向它所含的对象。在调用的时候调用这些函数而不是直接调用对象。理由(c++保证,函数内的local static对象会在该函数调用期间首次遇上该对象的定义式时被初始化)
程序修改之后:
class FileSystem { public: … size-tnumDisk() const; … }; FileSystem& tfs() { static FileSystem fs; returnfs; } Class Directory { Directory(params) { Size_tdisk=tfs().numDisks(); //调用函数tfs() } }; Directory& tempDir() { Static Directory td; return td; }
任何一种non-conststatic对象不论他是local还是non-local,在多线程的环境下等待某事发生都会有麻烦,处理麻烦的做法是:在程序单线程启动的阶段手工调用引用返回的函数,这可以消除与初始化有关的竞速形式。
总结:
一、对内置类型进行手工初始化(c++不保证初始化他们)。
二、构造函数最好使用成员初始列,而不要在构造函数内使用赋值操作,初始列的次序应该和class中的声明次序相同。
三、为免除跨编译单元的初始化问题,应该用local static对象替换non-local static对象。