C++類使用建構函式初始化類表和建構函式函式體中賦值的區別
參考:http://www.360doc.com/content/13/0607/19/1317564_291331713.shtml
C++ Primer中在講建構函式初始化列表的時候有這麼一段話:
無論是在建構函式初始化列表中初始化成員,還是在建構函式體中對它們賦值,最終結果是相同的。不同之處在於,使用建構函式初始化列表的版本初始化資料成員,沒有定義初始化列表的建構函式版本在建構函式體中對資料成員賦值。請問這裡的初始化資料成員與對資料成員賦值的含義是什麼?有什麼區別?
我知道在資料成員有預設建構函式時是有不同的,但對其他型別的成員呢?其他型別成員的初始化和賦值有區別嗎?
======================================================================================== 是這個意思:
首先把資料成員按型別分類
1。內建資料型別,複合型別(指標,引用) 2。使用者定義型別(類型別)
分情況說明:
對於型別1,在成員初始化列表和建構函式體內進行,在效能和結果上都是一樣的 對於型別2,結果上相同,但是效能上存在很大的差別
因為類型別的資料成員物件在進入函式體是已經構造完成,也就是說在成員初始化列表處進行構造物件的工作,這是呼叫一個建構函式,在進入函式體之後,進行的是對已經構造好的類物件的賦值,又呼叫個拷貝賦值操作符才能完成(如果並未提供,則使用編譯器提供的預設按成員賦值行為)
舉個例說明 class A; class B {public: B(){a = 3;} private: A a; }
class A {public: A(){}
A(int){value = 3;} int value; }
像上面,我們使a物件的value為3,呼叫一個A的建構函式+一個預設拷貝賦值符,才達到目的
B::B():a(3){}
像這樣,只調用了一個建構函式就達到了所需的物件啦,所以效能好的
轉載他人一篇
我的問題是關於初始化C++類成員的。我見過許多這樣的程式碼(包括在你的欄目中也見到過):
CSomeClass::CSomeClass() { x=0; y=1; }
而在別的什麼地方則寫成下面的樣子: CSomeClass::CSomeClass() : x(0), y(1) { }
我的一些程式設計師朋友說第二種方法比較好,但他們都不知道為什麼是這樣。你能告訴我這兩種類成員初始化方法的區別嗎?
回答
從技術上說,你的程式設計師朋友是對的,但是在大多數情況下,兩者實際上沒有區別。有兩個原因使得我們選擇第二種語法,它被稱為成員初始化列表:一個原因是必須的,另一個只是出於效率考慮。
讓我們先看一下第一個原因——必要性。設想你有一個類成員,它本身是一個類或者結構,而且只有一個帶一個引數的建構函式。 classCMember { public:
CMember(int x) { ... } };
因為Cmember有一個顯式宣告的建構函式,編譯器不產生一個預設建構函式(不帶引數),所以沒有一個整數就無法建立Cmember的一個例項。 CMember* pm = new CMember; // Error!! CMember* pm = new CMember(2); // OK
如果Cmember是另一個類的成員,你怎樣初始化它呢?你必須使用成員初始化列表。 classCMyClass { CMemberm_member; public: CMyClass(); };
//必須使用成員初始化列表
CMyClass::CMyClass() : m_member(2)
{ ••• }
沒有其它辦法將引數傳遞給m_member,如果成員是一個常量物件或者引用也是一樣。根據C++的規則,常量物件和引用不能被賦值,它們只能被初始化。
第二個原因是出於效率考慮,當成員類具有一個預設的建構函式和一個賦值操作符時。MFC的Cstring提供了一個完美的例子。假定你有一個類CmyClass具有一個Cstring型別的成員m_str,你想把它初始化為"yadayada."。你有兩種選擇: CMyClass::CMyClass() { // 使用賦值操作符
// CString::operator=(LPCTSTR); m_str = _T("yadayada"); }
//使用類成員列表
// and constructor CString::CString(LPCTSTR) CMyClass::CMyClass() : m_str(_T("yadayada")) { }
在它們之間有什麼不同嗎?是的。編譯器總是確保所有成員物件在建構函式體執行之前初始化,因此在第一個例子中編譯的程式碼將呼叫CString:: Cstring來初始化m_str,這在控制到達賦值語句前完成。在第二個例子中編譯器產生一個對CString:: CString(LPCTSTR)的呼叫並將"yadayada"傳遞給這個函式。結果是在第一個例子中呼叫了兩個Cstring函式(建構函式和賦值操作符),而在第二個例子中只調用了一個函式。在Cstring的例子裡這是無所謂的,因為預設建構函式是內聯的,Cstring只是在需要時為字串分配記憶體(即,當你實際賦值時)。但是,一般而言,重複的函式呼叫是浪費資源的,尤其是當建構函式和賦值操作
當我考慮初始化列表的問題時,有一個奇怪的特性我應該警告你,它是關於C++初始化類成員的,它們是按照宣告的順序初始化的,而不是按照出現在初始化列表中的順序。 classCMyClass { CMyClass(int x, int y); intm_x; intm_y; };
CMyClass::CMyClass(inti) : m_y(i), m_x(m_y) { }
你可能以為上面的程式碼將會首先做m_y=I,然後做m_x=m_y,最後它們有相同的值。但是編譯器先初始化m_x,然後是m_y,,因為它們是按這樣的順序宣告的。結果是m_x將有一個不可預測的值。我的例子設計來說明這一點,然而這種bug會更加自然的出現。有兩種方法避免它,一個是總是按照你希望它們被初始化的順序宣告成員,第二個是,如果你決定使用初始化列表,總是按照它們宣告的順序羅列這些成員。這將有助於消除混淆。