將對象初始化
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對象,它所用到的這個對象可能未被初始化。
例:
假設用戶建立一個classclass FileSystem{ public: ... std::size_t numDisks()const; //衆多成員函數之一 ... } extern FileSystem tfs; //預備給客戶使用的對象
如果客戶決定創建一個Director對象class Directory{ public: Directory(params); ... }; Directory::Directory(params) { ... std::size_t disks=tfs.numDisks(); //使用tfs對象 ... }
除非tfs在tempDir之前被初始化,否則tempDir的構造函數會用到尚未初始化的tfs。它們是定義在不同編譯單元內的non-local static 對象。Director tempDir (params);
解決:
將每個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 對象。