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;
}