構造函數初始化列表以一個冒號開始,接着是以逗號分隔的數據成員列表,每一個數據成員後面跟一個放在括號中的初始化式。例如:函數
class A { public: int a; float b; A(): a(0),b(9.9) {} //構造函數初始化列表 }; class A { public: int a; float b; A() //構造函數內部賦值 { a = 0; b = 9.9; } };
上面的例子中兩個構造函數的效果是同樣的。使用初始化列表的構造函數是顯示地初始化類的成員;而沒有使用初始化列表的構造函數是對類的成員賦值,並無顯示地初始化。spa
初始化列表的構造函數和內部賦值的構造函數對內置類型的成員沒有什麼大的區別,像上面的任一個構造函數均可以。
用構造函數的初始化列表來進行初始化,寫法方便、簡練,尤爲當須要初始化的數據成員較多時更顯其優越性。對非內置類型成員變量,推薦使用類構造函數初始化列表。
但有的時候必須用帶有初始化列表的構造函數:(1)沒有默認構造函數的成員類對象;(2)const成員或引用類型的成員。 code
構造函數中有着比咱們所看見的還要多的細節,構造函數能夠調用其它的構造函數來初始化對象中的基類對象和成員對象的構造函數。
類的數據成員中的其它類對象,若該成員類型是沒有默認構造函數,則必須進行顯示初始化,由於編譯器會隱式調用成員類型的默認構造函數,而它又沒有默認構造函數,則編譯器嘗試使用默認構造函數將會失敗。
對象
例:blog
class A { public: A (int x) { i = x; // 無默認構造函數 } private: int i; }; class B { public: B(int y) { j = y; // error C2512: 「A」: 沒有合適的默認構造函數可用 } private: A a; int j; }; int main( ) { B b(5); return 0; }
B類數據成員中有一個A類對象a,建立B類對象時,要先建立其成員對象a;A類有一個參數化大的構造函數,則編譯器不會提供默認無參數的構造函數,所以a沒法建立。編譯器
對成員對象正確的初始化方法是經過顯示方式進行,B的構造函數應該寫成:string
B(int y, int z) : a(z) { j = y; } B b(5,10);
構造函數初始化列表是初始化常數據成員和引用成員的惟一方式。由於const對象或引用類型只能初始化,不能對他們賦值。
編譯
class A { public: A (int x,int y) : c(x),j(y) // 構造函數初始化列表 { i = -1; } private: int i; const int c; int& j; }; int main( ) { int m; A a(5,m); return 0; }
若不經過初始化列表來對常數據成員和引用成員進行初始化:class
class A { public: A (int x) // 構造函數初始化列表 { i = -1; c = 5; j = x; } private: int i; const int c; // error C2758: 「A::c」: 必須在構造函數基/成員初始值設定項列表中初始化 int& j; // error C2758: 「A::j」: 必須在構造函數基/成員初始值設定項列表中初始化 };
缺省狀況下,在構造函數的被執行前,對象中的全部成員都已經被它們的缺省構造函數初始化了。當類中某個數據成員自己也是一個類對象時,咱們應該避免使用賦值操做來對該成員進行初始化:效率
class Person { private: string name; public: Person(string& n) { name = n; } }
雖然這樣的構造函數也能達到正確的結果,但這樣寫效率並不高。當一個Person對象建立時,string類成員對象name先會被缺省構造函數進行初始化,而後在Person類構造函數中,它的值又會因賦值操做而在改變一次。咱們能夠經過初始化列表來顯示地對name進行初始化,這樣便將上邊的兩步(初始化和賦值)合併到一個步驟中了。
class Person { private: string name; public: Person(string& n): name(n){} }