首先,這篇文章實際上不是我自己的話。它摘自一本很棒的書
"Effective C++:改善程式和設計的 55 個具體方法",作者是
Scott Meyers,我認為有必要為它寫一篇文章,因為它提供了一些非常有用的資訊。
儘量不要糾結於前幾句話,因為這是章節中的一部分...
對於幾乎所有其他事情,初始化的責任都落在建構函式上。那裡的規則很簡單:確保所有建構函式都初始化物件中的所有內容。
這個規則很容易遵循,但重要的是不要將賦值與初始化混淆。考慮一個表示地址簿中條目的類的建構函式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
Class PhoneNumber { … };
Class ABEntry { // ABEntr = “Address Book Entry”
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(const std::String& name, const std::string& address,
const std::list<PhoneNumber>& phones)
{
theName = name; // these are all assignments,
theAddress = address; // no initializations
thePhones = phones;
numTimesConsulted = 0;
}
|
這將產生具有您期望值的 ABEntry 物件,但這仍然不是最好的方法。C++ 的規則規定,物件的資料成員
在進入建構函式體
之前被初始化。在 ABEntry 建構函式內部,theName、theAddress 和 thePhones 沒有被初始化,它們正在被賦值。初始化發生在更早的時候——當它們的預設建構函式在進入 ABEntry 建構函式體之前被自動呼叫時。對於 numTimesConsulted 來說,情況並非如此,因為它是一個內建型別。對於它,不能保證在賦值之前被初始化。
編寫 ABEntry 建構函式的更好方法是使用成員初始化列表而不是賦值
1 2 3 4 5 6 7
|
const std::String& name, const std::string& address,
const std::list<PhoneNumber>& phones)
: theName(name),
theAddress(address), // these are now all initializations
thePhones(phones),
numTimesConsulted(0)
{} // the ctor body is now empty
|
此建構函式產生與上面相同的結果,但通常效率更高。基於賦值的版本首先呼叫預設建構函式來初始化 theName、theAddress 和 thePhones,然後迅速在預設構造的物件之上分配新值。因此,在這些預設構造中執行的所有工作都被浪費了。成員初始化列表方法避免了這個問題,因為初始化列表中的引數用作各種資料成員的建構函式引數。在這種情況下,theName 從 name 複製構造,theAddress 從 address 複製構造,thePhones 從 phones 複製構造。對於大多數型別,對複製建構函式的單次呼叫比呼叫預設建構函式後跟呼叫複製賦值運算子更有效——有時
效率高得多。
對於像 numTimesConsulted 這樣的內建型別的物件,初始化和賦值的成本沒有差異,但為了保持一致性,通常最好透過成員初始化來初始化所有內容。同樣,即使您想預設構造一個數據成員,也可以使用成員初始化列表;只需指定沒有任何初始化引數即可。