1.區分初始化和賦值
在C++中,當一個新對象被創建時,會有初始化操作;而賦值是修改一個已經存在的對象的值。
初始化操作由構造函數完成,而賦值操作由operator=操作符函數完成。如果程序員沒有提供構造函數和operator=操作符函數,那么編譯器會提供缺省版本,使用缺省構造函數或缺省復制構造函數初始化對象,使用缺省operator=操作符函數執行賦值操作。舉例如下:

/** * @file Main.cpp */ #include <iostream> using std::cout; using std::endl; class CDummy { public: CDummy(void); CDummy(const CDummy& dummy); CDummy& operator=(const CDummy& dummy); }; CDummy::CDummy() { cout << "無參構造" << endl; } CDummy::CDummy(const CDummy& /*dummy*/) { cout << "復制構造" << endl; } CDummy& CDummy::operator=(const CDummy& /*dummy*/) { cout << "賦值操作" << endl; return *this; } int main(int argc, char* argv[]) { CDummy d1; //初始化,無參構造函數被調用 CDummy d2 = d1; //初始化,復制構造函數被調用 CDummy d3; //初始化,無參構造函數被調用 d3 = d1; //賦值,operator=操作符函數被調用 return 0; }
另外要注意,在賦值時被賦值對象已經有值,因此可能需要對原值做適當處理,特別是資源的歸還和重新申請等等。在這方面,String類的operator=操作符函數的實現是典型例子。
2.關於成員初始化列表和構造函數內賦值操作
先看示例程序:

class PhoneNumber { }; class AddressBookEntry { public: AddressBookEntry(const string& name, const string& address, const list<PhoneNumber>& phones); private: string _name; string _address; list<PhoneNumber> _phones; }; AddressBookEntry::AddressBookEntry(const string& name, const string& address, const list<PhoneNumber>& phones) { _name = name; //這些都是賦值,而非初始化 _address = address; _phones = phones; }
C++規定,對象的成員變量的初始化動作發生在進入構造函數本體之前。在AddressBookEntry構造函數內,_name,_address,_phones都不是被初始化,而是被賦值。初始化的發生時間更早,發生於這些成員的default構造函數被自動調用之時(比進入AddressBookEntry構造函數本體的時間更早)。
AddressBookEntry構造函數的一個較佳寫法是,使用成員初始化列表代替賦值動作:

AddressBookEntry::AddressBookEntry(const string& name, const string& address, const list<PhoneNumber>& phones) :_name(name), //現在,這些都是初始化 _address(address), _phones(phones) { }
賦值的那個版本先調用default構造函數然后再調用operator=操作符函數;而成員初始化列表的那個版本只調用一次copy構造函數,是比較高效的,有時甚至高效得多。
另外,對於內置類型對象,其初始化和賦值的成本相同。但是有些情況下即使成員變量屬於內置類型,也一定得使用成員初始化列表。是的,如果成員變量是 const 或 references,它們就一定需要初值,不能被賦值。
3.關於成員初始化次序
C++有着十分固定的成員初始化次序。是的,次序總是相同:base classes 更早於其 derived classes被初始化,而 class 的成員變量總是以其聲明次序被初始化。
另外,為避免“跨編譯單元之初始化次序”問題,請以 local static 對象替換 non-local static 對象。這是 Singleton 模式的一個常見實現手法。(ACE里面的單例大多是這么實現的,原來是有原因的:))
參考文獻:
Effective C++ 3rd Edition