构造函数使用本体括号进行复制的情况不叫做初始化而是叫做赋值语句,只有在成员初始化列表中直接进行的 才叫做初始化。
ABentry::AbEntry(const std::string & name, const std::string & address, const std::list<PhoneNumber> & phones) :thename(name), address(address), numTimesconsulted(0) {}
初始化列表相比在构造函数之中那通过复制初始化效率往往更高,那个版本是首先调用默认的构造函数来为几个private成员来初始化,之后再通过函数的主体部分给他们来赋值,但是后面这一个版本成员函数所规定的实参都被拿去作为各自的构造函数的实际参数了。
还有一种情况是当某些成员变量是内置类型的时候,如果其定义的时候是const或者reference的话,也应定要使用成员初始化列表来进行初始化。
c++有着十分固定的成员初始化的次序,base class早于derived class进行初始化,而class成员总是根据他被生命的次序来被初始化。计时在成员初始化列表中医不同与声明class中的次序出现来进行初始化,还是会按照原来声明的顺序来进行初始化。
应该注意不同编译单元内的定义的non-local static对象的初始化顺序。这里的编译单元指的是产出单一的目标文件的那些源码,基本上是单一的源码文件加上其所附属的头文件。例如下面的这个例子:
class FileSystem{ public: ... std::size_t numDisks () const ; ... } extern FileSystem tfs; //the file system这是是在服务器上的,但是准备留给用户来使用的。 //但是考虑一下,如果用户在 thefilesystem构造完成之前就使用它,那么便会带来灾难。 //例如下面用户使用的那样 class Directory{ public: Directory(paras ); ... }; Directory::Directory (paras) { ... std::size_t disk = tfs. numDisks;// 这里就有可能使用到未初始化的 tfs,可能会带来错误 ... }//这个时候如果用户创建一个 Directory对象的话: Directory tempDir(params ); //那么只有当tfs在 tempDir之前被定义了的时候才保证不会发生错误
但是我们可以使用一些技巧来避免这一种情况,就是将每个non-local static对象搬到自己的专属函数之中,即该对象在该函数中是static的,函数返回一个reference来只想这些对象,之后对象调用这些函数,但是不是直接涉及这些对象。这样non-local static对象就被一个local static对象替代了。这个手打的关键在于,c++保证,函数内的local-static对象会在该函数的调用期间,首次遇上该对象的定义式时被初始化。这样通过函数调用来替代直接使用对应的non-local static对象,就获得了一种保证,使得所获得的那个reference将会指向一个经历了初始化的过程,而且只要没有调用non-local static对象的仿真函数,就绝对不会引发构造以及析构成本,这对于单纯的non-local static函数来说是不可能的。实际上的应用过程如下所示。
class
FileSystem{...};
//与前面相同
FileSystem & tfs() { static FileSystem fs; return fs; } class Directory(...);//与前面相同 Directory::Directory(params) { ... std::size_t disks = tfs().numDisks(); ... } Directory& tempDir() { static Directory td; return td; } //现在的程序与之前的区别就是将纯粹的non-localeconv static对象变成了返回指向static对象的reference
小结:
- 为内置类型进行手动的初始化,因为c++不会保证初始化他们
- 构造函数应该使用成员初始化列表,而不应该使用普通的赋值操作
- 为避免“便一单元之初始化次序的额问题”,应该以local static对象来替换non-local static 对象