與其他
函式不同,
建構函式除了有名字,
引數列表和
函式體之外,還可以有初始化列表,
初始化列表以冒號開頭,後跟一系列以逗號分隔的初始化字段。
class foo
; // 初始化列表
private:
string name ;int id ;
};初始化類的成員有兩種方式,一是使用初始化列表,二是在建構函式體內進行賦值操作。
主要是效能問題,對於
內建型別,如int, float等,使用初始化類表和在
建構函式體內初始化差別不是很大,但是對於類型別來說,最好使用初始化列表,為什麼呢?由下面的測試可知,使用初始化列表少了一次呼叫
預設建構函式的過程,這對於資料密集型的類來說,是非常高效的。同樣看上面的例子,我們使用初始化列表來實現test2的建構函式。
class test1
test1(const test1& t1) //
拷貝建構函式
test1& operator = (const test1& t1) //
賦值運算子
int a ;
};struct test2
} 使用同樣的呼叫**,輸出結果如下:
construct test1
copy constructor for test1
第一行輸出對應 呼叫**的第一行
第二行輸出對應test2的初始化列表,直接呼叫
拷貝建構函式初始化test1,省去了呼叫
預設建構函式的過程。
所以乙個好的原則是,能使用初始化列表的時候盡量使用初始化列表
在使用c++程式設計的過程當中,常常需要對類成員進行初始化,通常的方法有兩種:第一種方法:
cmyclass::csomeclass()
第二種方法:
csomeclass::csomeclass() : x(0), y(1)
本文將要**這兩種方法的異同以及如何使用這兩種方法。
從技術上說,第二種方法比較好,但是在大多數情況下,兩者實際上沒有什麼區別。第二種語法被稱為成員初始化列表,之所以要使用這種語法有兩個原因:乙個原因是必須這麼做,另乙個原因是出於效率考慮。
讓我們先看一下第乙個原因——必要性。設想你有乙個類成員,它本身是乙個類或者結構,而且只有乙個帶乙個引數的建構函式。
class cmember };
因為cmember有乙個顯式宣告的建構函式,編譯器不產生乙個預設建構函式(不帶引數),所以沒有乙個整數就無法建立cmember的乙個例項。
cmember* pm = new cmember; // 出錯!! cmember* pm = new cmember(2); // ok
如果cmember是另乙個類的成員,你怎樣初始化它呢?答案是你必須使用成員初始化列表。
class cmyclass ; // 必須使用初始化列表來初始化成員 m_member cmyclass::cmyclass() : m_member(2)
沒有其它辦法將引數傳遞給m_member,如果成員是乙個常量物件或者引用也是一樣。根據c++的規則,常量物件和引用不能被賦值,它們只能被初始化。
使用初始化列表的第二個原因是出於效率考慮,當成員類具有乙個預設的建構函式和乙個賦值操作符時。mfc的cstring提供了乙個完美的例子。假定你有乙個類cmyclass具有乙個cstring型別的成員m_str,你想把它初始化為"hi,how are you."。你有兩種選擇:
cmyclass::cmyclass()
// 使用初始化列表 // 和建構函式 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函式。在這種情況下,你必須使用初始化列表,以避免不要的分配兩次記憶體。
在內建型別如ints或者longs或者其它沒有建構函式的型別下,在初始化列表和在建構函式體內賦值這兩種方法沒有效能上的差別。不管用那一種方法,都只會有一次賦值發生。有些程式設計師說你應該總是用初始化列表以保持良好習慣,但我從沒有發現根據需要在這兩種方法之間轉換有什麼困難。在程式設計風格上,我傾向於在主體中使用賦值,因為有更多的空間用來格式化和新增注釋,你可以寫出這樣的語句:
x=y=z=0;
或者 memset(this,0,sizeof(this));
注意第二個片斷絕對是非物件導向的。
當我考慮初始化列表的問題時,有乙個奇怪的特性我應該警告你,它是關於c++初始化類成員的,它們是按照宣告的順序初始化的,而不是按照出現在初始化列表中的順序。
class cmyclass ; cmyclass::cmyclass(int i) : m_y(i), m_x(m_y)
你可能以為上面的**將會首先做m_y=i,然後做m_x=m_y,最後它們有相同的值。但是編譯器先初始化m_x,然後是m_y,,因為它們是按這樣的順序宣告的。結果是m_x將有乙個不可**的值。這個例子是故意這樣設計來說明這一點的,然而這種bug會很自然地出現。有兩種方法避免它,乙個是總是按照你希望它們被初始化的順序來宣告成員,第二個是,如果你決定使用初始化列表,總是按照它們宣告的順序羅列這些成員。這將有助於消除混淆。
c 初始化列表
與其他函式不同,建構函式除了有名字,引數列表和函式體之外,還可以有初始化列表,初始化列表以冒號開頭,後跟一系列以逗號分隔的初始化字段。struct foo 初始化列表 建構函式的兩個執行階段 從概念上來講,建構函式的執行可以分成兩個階段,初始化階段和計算階段,初始化階段先於計算階段 初始化階段 所有...
C 初始化列表
與其他函式不同,建構函式除了有名字,引數列表和函式體之外,還可以有初始化列表,初始化列表以冒號開頭,後跟一系列以逗號分隔的初始化字段。在c 中,struct和class的唯一區別是預設的克訪問性不同,而這裡我們不考慮訪問性的問題,所以下面的 都以struct來演示。struct foo 初始化列表 ...
C 初始化列表
與其他函式不同,建構函式除了有名字,引數列表和函式體之外,還可以有初始化列表,初始化列表以冒號開頭,後跟一系列以逗號分隔的初始化字段。struct foo 初始化列表 建構函式的執行可以分成兩個階段,初始化階段和計算階段,初始化階段先於計算階段。所有類型別 class type 的成員都會在初始化階...