類物件的構造順序是這樣的:
1.分配記憶體,呼叫建構函式時,隱式/顯示的初始化各資料成員;
2.進入建構函式後在建構函式中執行一般賦值與計算。
使用初始化列表有兩個原因:
原因1.必須這樣做:
《c++ primer》中提到在以下三種情況下需要使用初始化成員列表:
情況一、需要初始化的資料成員是物件的情況(這裡包含了繼承情況下,通過顯示呼叫父類的建構函式對父類資料成員進行初始化);
情況二、需要初始化const修飾的類成員;
情況三、需要初始化引用成員資料;
情況一的說明:資料成員是物件,並且這個物件只有含引數的建構函式,沒有無引數的建構函式;
如果我們有乙個類成員,它本身是乙個類或者是乙個結構,而且這個成員它只有乙個帶引數的建構函式,而沒有預設建構函式,這時要對這個類成員進行初始化,就必須呼叫這個類成員的帶引數的建構函式,如果沒有初始化列表,那麼他將無法完成第一步,就會報錯。
例如:
#include "iostream"
using namespace std;
class test
; private:
int x;
int y;
int z;
};class mytest
;private:
test test; //宣告
};int _tmain(int argc, _tchar* argv)
①如果沒有mytest():test(1,2,3){}初始化列表就會報錯:
因為test有了顯示的帶引數的建構函式,那麼他是無法依靠編譯器生成無參建構函式的,所以沒有三個int型資料,就無法建立test的物件。test類物件是mytest的成員,想要初始化這個物件test,那就只能用成員初始化列表,沒有其他辦法將引數傳遞給test類建構函式。
②初始化列表在建構函式執行前執行(這個可以看上面的結果,對同乙個變數在初始化列表和建構函式中分別初始化,首先執行引數列表,後在函式體內賦值,後者會覆蓋前者)。
情況二的說明:物件引用或者cosnt修飾的資料成員
當類成員中含有乙個const物件時,或者是乙個引用時,他們也必須要通過成員初始化列表進行初始化,因為這兩種物件要在宣告後馬上初始化,而在建構函式中,做的是對他們的賦值,這樣是不被允許的。
例子:
class test
//初始化};或
class test
//初始化
}
情況三的說明:子類初始化父類的私有成員,需要在(並且也只能在)引數初始化列表中顯示呼叫父類的建構函式:如下:
class test;
test (int x);
void show()
private:
int int_x;
};class mytest:public test;
};int _tmain(int argc, _tchar* argv)
結果:如果在建構函式內部被顯示呼叫輸出結果是:1100;
如果在初始化列表中被顯示呼叫輸出結果是:-842150451(原因是這裡呼叫了無參建構函式)
原因2.效率要求這樣做:
類物件的構造順序顯示,進入建構函式體後,進行的是計算,是對成員變數的賦值操作,顯然,賦值和初始化是不同的,這樣就體現出了效率差異,如果不用成員初始化列表,那麼類對自己的類成員分別進行的是一次隱式的預設建構函式的呼叫,和一次賦值操作符的呼叫,如果是類物件,這樣做效率就得不到保障。
注意:建構函式需要初始化的資料成員,不論是否顯示的出現在建構函式的成員初始化列表中,都會在該處完成初始化,並且初始化的順序和其在類中宣告時的順序是一致的,與列表的先後順序無關,所以要特別注意,保證兩者順序一致才能真正保證其效率和準確性。
為了說明清楚,假設有這樣乙個類:
class
foo;
①、foo(){}和foo(int i = 0){}都被認為是預設建構函式,因為後者是預設引數。兩者不能同時出現。
②建構函式列表的初始化方式不是按照列表的的順序,而是按照變數宣告的順序。比如foo裡面,a在b之前,那麼會先構造a再構造b。所以無論foo():a(b + 1), b(2){}還是foo():b(2),a(b+1){}都不會讓a得到期望的值。
③建構函式列表能夠對const成員初始化。比如foo裡面有乙個int const c;則foo(int x) : c(x){}可以讓c值賦成x。
不過需要注意的是,c必須在每個建構函式(如果有多個)都有值。
④在繼承裡面,只有初始化列表可以構造父類的private成員(通過顯示呼叫父類的建構函式)。比如說:
class child : public foo{};
foo裡面的建構函式是這樣寫的:
foo (int x)
. 而在child裡面寫child(int x)是通過不了編譯的。
只有把子類建構函式寫作child (int x) : foo(x){}才可以。
強調的地方:
一 1.類裡面的任何成員變數在定義時是不能初始化的。
2.一般的資料成員可以在建構函式中初始化。
3.const資料成員必須在建構函式的初始化列表中初始化。
4.static要在類的定義外面初始化。
5.陣列成員是不能在初始化列表裡初始化的。
6.不能給陣列指定明顯的初始化。
這6條一起,說明了乙個問題:c++裡面是不能定義常量陣列的!因為3和5的矛盾。這個事情似乎說不過去啊?沒有辦法,我只好轉而求助於靜態資料成員。
到此,我的問題解決。但是我還想趁機複習一下c++類的初始化:
1.初始化列表:csomeclass::csomeclass() : x(0), y(1){}
2.類外初始化:int csomeclass::myvar=3;
3.const常量定義必須初始化,c++類裡面使用初始化列表;
4.c++類不能定義常量陣列。
二.這裡再強調一下類的初始化的順序,應該是類成員變數的初始化不是按照初始化表的順序被初始化的,而是按照在類中宣告的順序被初始化的。
這是摘自:effective c++學習筆記:初始化列表中成員列出的順序和它們在類中宣告的順序相同.
為什麼會這樣呢?我們知道,對乙個物件的所有成員來說,它們的析構函式被呼叫的順序總是和它們在建構函式裡被建立的順序相反。那麼,如果允許上面的情況(即,成員按它們在初始化列表上出現的順序被初始化)發生,編譯器就要為每乙個物件跟蹤其成員初始化的順序,以保證它們的析構函式以正確的順序被呼叫。這會帶來昂貴的開銷。所以,為了避免這一開銷,同一種型別的所有物件在建立(構造)和摧毀(析構)過程中對成員的處理順序都是相同的,而不管成員在初始化列表中的順序如何。
注意:上述內容不適用於static變數,static變數應該在類的建構函式前被初始化。
初始化 1 預設初始化 列表初始化
初始化的基本概念 事實 初始化和賦值是兩個完全不同的操作。初始化,是建立變數時賦予其乙個初始值。賦值,是把物件的當前值擦除,用乙個新值代替。列表初始化 p39 作為c 11新標準的一部分,用花括號 來初始化變數得到了全面應用。出於某些原因,這種初始化的方式叫做列表初始化。現在,無論是初始化物件還是某...
何時必須使用初始化列表
與其他函式不同,建構函式除了有名字,引數列表和函式體之外,還可以有初始化列表,初始化列表以冒號開頭,後跟一系列以逗號分隔的初始化字段。在c 中,struct和class的唯一區別是預設的訪問性不同,而這裡我們不考慮訪問性的問題,所以下面的 都以struct來演示。struct foo 初始化列表 建...
初始化列表
與其他函式不同,建構函式除了有名字,引數列表和函式體之外,還可以有初始化列表,初始化列表以冒號開頭,後跟一系列以逗號分隔的初始化字段。在c 中,struct和class的唯一區別是預設的克訪問性不同,而這裡我們不考慮訪問性的問題,所以下面的 都以struct來演示。struct foo 初始化列表 ...