Item04 确定对象被使用之前已被初始化
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> thePhone;
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构造函数内,theName,theAddress和thePhone都不是被初始化而是被赋值。他首先调用default构造函数为theName,theAddress和thePhone设初值,然后立刻再对他们赋予新值。default构造函数的一切作为浪费了。
ABEntry::ABEntry(const std::string& name,const std::string& address, const std::list<PhoneNumber>& phones)
:theName(name),theAddress(address),thePhones(phones),numTimesConsulted(0)
{}
成员初值列(member initialization list)的做法避免了这一问题,因为初值列中针对各个成员变量而设的实参,被拿去作为各成员变量之构造函数的实参。本例只调用一次copy构造函数是比较高效的。
C++有着十分固定的“成员初始化次序”。初始化array时需要指定大小,因此代表大小的那个成员变量必须现有初值。
不同编译单元内定义之non-local static对象的初始化次序
C++对此并无明确定义。
static对象,其寿命从被构造出来知道程序结束,因此staack和heap-based对象被排除。这种对象包括global对象、定义于namespace作用域内的对象、在class内、在函数内(local static)、以及在file作用域内被声明为static的对象。
class FileSystem{ //来自你的程序库
public:
...
std::size_t numDisks()const;
...
}
extern FileSystem tfs; //预备给客户使用的对象;
class Directory{ //由程序库客户建立
public:
Directory(params);
...
}
Directory::Directory(params){
...
std::size_t disks=tfs.numDisks(); //使用tfs对象
}
Directory tempDir(params); //为临时文件而做出的目录
tfs必须在tempDir之前先被初始化,但tfs和tempDir是不同的人在不同的时间于不同的源文件建立起来的,他们是定义于不同编译单元内的non-local static对象,其次序无法确定。
解决办法:将每个non-local static对象搬到自己的专属函数内(该对象在次函数内被声明为static).这些函数返回一个reference指向它所含的对象。non-local static对象被local static对象替换了。
此技术施于tfs和tempDir身上:
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;
}