effect C++ 确定对象被使用前先被初始化

来源:互联网 发布:初中考试软件 编辑:程序博客网 时间:2024/06/05 14:12

将对象初始化

int x 在某些语境下x保证初始化(为0),但在其他语境中却不保证。

class Point{  int x , int y;} .... Point p ;

p的成员变量有时候初始化(为0),有时不会。读取未初始化的值会导致不明确行为。

最佳的处理办法就是:永远在使用对象之前将他们初始化。对于无任何成员的内置类型,必须手工完成此事。

int x =0;        const char * text ="A-C-style string";double d ;std::cin>>d;      //以读取input stream的方式完成初始化

确保每一个构造函数都将对象的每一个成员初始化。

注意别混淆了赋值和初始化。


class PhoneNumber {...}class ABEntry{public:  ABEntry(const std::string& name,const std::string&address,const std::list<PhoneNumber>&phones);private:   std:: string theName;   std:: string theAddress;   std:: list<PhoneNumber>thePhones;     int numTimesConsulted;};ABEntry::ABEntry(const std::string&name,const std::string &address,const std::list<PhoneNumber>&phones){    theName=name;         //这些都是赋值    theAddress=address;   //而非初始化    thePhones=phones;    numTimesConsulted=0;}


C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前

较佳的一个写法是:

ABEntry::ABEntry(const std:: string &name,const std::string&address,  const std::list<PhoneNumber>&phone):theName(name),theAddress(address),thePhones(phones),numTimesConsulted(0) //这些都是初始化{}

通常效率更高,基于赋值的那个版本首先调用default构造函数为成员赋初值,然后立刻重新赋值。

总使用成员初值列。

不同编译单元内定义之non-local static 对象

static对象 ,其寿命从构造出来直到程序结束为止(stack和heap-based对象都被排除)。这种对象包括global对象,定义于namesapce作用域的对象、在classes内、函数内、以及在file作用域内被声明为static的对象。 函数内的static对象被称为local static对象(因为它们对函数而言是local),其他的对象称为non-local对象。程序结束时static对象会被自动销毁,它们的析构函数会在main()结束时调用。

编译单元是产出单一目标文件的那些源码,基本是单一源码加上其所含入的头文件。

现在至少有两个源码文件,每一个内含至少一个non-local static 对象(也就是该对象是global或位于namespace作用域内,抑或在class内或file作用域内被声明为static)。真正的问题是:如果某编译单元内某个non-local static对象的初始化动作使用了另一编译单元内的某个non-local static对象,它所用到的这个对象可能未被初始化。

例:

class FileSystem{public:...std::size_t numDisks()const; //众多成员函数之一...}extern FileSystem tfs; //预备给客户使用的对象
假设用户建立一个class

class Directory{public: Directory(params); ...};Directory::Directory(params){ ... std::size_t disks=tfs.numDisks(); //使用tfs对象 ...}
如果客户决定创建一个Director对象

Director tempDir (params);
除非tfs在tempDir之前被初始化,否则tempDir的构造函数会用到尚未初始化的tfs。它们是定义在不同编译单元内的non-local static 对象。

解决:
将每个non-local static 对象搬到自己的专属函数内(该对象在此函数内被声明为static)。这些函数返回一个reference 指向它所含的对象。然后用户调用着这些函数,而不是指涉这些对象。 non-local static 对象被 local static 对象替换了。(Singleton模式的一个常见的实现手法)

C++保证,函数内的local static 对象会在“该函数被调用期间”“首次遇上该对象之定义式”时被初始化。所以如果以“函数调用”(返回一个reference指向local static对象)替换“直接访问non-local static对象”,就可以保证获得的那个reference将指向一个历经初始化的对象。如果从未调用 non-local static 对象的“仿真函数”,就绝不会引发构造和析构成本。

class FileSystem{...}    //同前FileSystem& tfs()        //这个函数用来替换tfs对象;它在FileSystem class 中可能是个static{  static FileSystem fs;  //定义并初始化一个local static对象  return fs;             //返回一个reference指向上述对象}class Directory{...};      //同前Directory::Directory(params){   ...   std::size_t disks =tfs().numDisks();//原本的reference to tfs现在改为tfs()}Directory&tempDir()    //这个函数用来替换tempDir对象,它在Directory中可能是一个static  {  static Directory td;   //定义并初始化local static 对象  return td;            //返回一个reference指向上述对象}

这样,客户使用tfs()和tempDir()而不再是tfs和tempDir,使用的是指向static对象的reference


为内置型对象进行手工初始化,因为C++不保证初始化它们。

构造函数最后使用成员初值列,而不要在构造函数本体内使用赋值操作。初值列列初的成员列表,其排列次序应该和它们在class中声明的次序相同。

为免除“跨编译单元之初始化次序”问题,请以local static 对象替换non-local static 对象。

0 0