在使用C++編程的過程當中,常常需要對類成員進行初始化,通常的方法有兩種:
第一種方法:
CMYClass::CSomeClass()
{
x=0;
y=1;
}
第二種方法:CSomeClass::CSomeClass() : x(0), y(1)
{
}
本文將要探討這兩種方法的異同以及如何使用這兩種方法。
從技術上說,第二種方法比較好,但是在大多數情況下,兩者實際上沒有什麼區別。第二種文法被稱為成員初始化列表,之所以要使用這種文法有兩個原因:一個原因是必須這麼做,另一個原因是出於效率考慮。
讓我們先看一下第一個原因——必要性。設想你有一個類成員,它本身是一個類或者結構,而且只有一個帶一個參數的建構函式。class CMember {
public:
CMember(int x) { ... }
};
因為CMember有一個顯式聲明的建構函式,編譯器不產生一個預設建構函式(不帶參數),所以沒有一個整數就無法建立CMember的一個執行個體。CMember* pm = new CMember; // 出錯!!
CMember* pm = new CMember(2); // OK
如果CMember是另一個類的成員,你怎樣初始化它呢?答案是你必須使用成員初始化列表。class CMyClass {
CMember m_member;
public:
CMyClass();
};
// 必須使用初始化列表來初始化成員 m_member
CMyClass::CMyClass() : m_member(2)
{
•••
}
沒有其它辦法將參數傳遞給m_member,如果成員是一個常量對象或者引用也是一樣。根據C++的規則,常量對象和引用不能被賦值,它們只能被初始化。
使用初始化列表的第二個原因是出於效率考慮,當成員類具有一個預設的建構函式和一個賦值操作符時。MFC的CString提供了一個完美的例子。假定你有一個類CMyClass具有一個CString類型的成員m_str,你想把它初始化為"Hi,how are you."。你有兩種選擇:
CMyClass::CMyClass() {
// 使用賦值操作符
// CString::operator=(LPCTSTR);
m_str = _T("Hi,how are you.");
}
// 使用初始化列表
// 和建構函式 CString::CString(LPCTSTR)
CMyClass::CMyClass() : m_str(_T("Hi,how are you."))
{
}
在它們之間有什麼不同嗎?是的。編譯器總是確保所有成員對象在建構函式體執行之前被初始化,因此在第一個例子中編譯的代碼將調用CString::Cstring來初始化m_str,這在控制到達指派陳述式前完成。在第二個例子中編譯器產生一個對CString:: CString(LPCTSTR)的調用並將"Hi,how are you."傳遞給這個函數。結果是在第一個例子中調用了兩個CString函數(建構函式和賦值操作符),而在第二個例子中只調用了一個函數。
在CString的例子裡這是無所謂的,因為預設建構函式是內聯的,CString只是在需要時為字串分配記憶體(即,當你實際賦值時)。但是,一般而言,重複的函數調用是浪費資源的,尤其是當建構函式和賦值操作符分配記憶體的時候。在一些大的類裡面,你可能擁有一個建構函式和一個賦值操作符都要調用同一個負責分配大量記憶體空間的Init函數。在這種情況下,你必須使用初始化列表,以避免不要的分配兩次記憶體。