C++為類中提供類成員的初始化列表
類對象的構造 順序是這樣的:
1.分配記憶體,調用建構函式 時,隱式/顯示的初始化各資料 成員
2.進入建構函式後在建構函式中執行一般計算
使用初始化列表有兩個原因:
1.必須這樣做:
如果我們有一個類成員,它本身是一個類或者是一個結構,而且這個成員它只有一個帶參數的建構函式,而沒有預設建構函式,這時要對這個類成員進行初始化,就必須調用這個類成員的帶參數的建構函式,如果沒有初始化列表,那麼他將無法完成第一步,就會報錯。
複製代碼 代碼如下:
class ABC
... {
public :
ABC( int x, int y, int z);
private :
int a;
int b;
int c;
} ;
class MyClass
... {
public :
MyClass():abc( 1 , 2 , 3 ) ... {}
private :
ABC abc;
} ;
因為ABC有了顯示的帶參數的建構函式,那麼他是無法依靠編譯器產生無參建構函式的,所以沒有三個int型資料,就無法建立ABC的對象。
ABC類對象是MyClass的成員,想要初始化這個對象abc,那就只能用成員初始化列表,沒有其他辦法將參數傳遞給ABC類建構函式。
另一種情況是這樣的:當類成員中含有一個const對象時,或者是一個引用時,他們也必須要通過成員初始化列表進行初始化,因為這兩種對象要在聲明後馬上初始化,而在建構函式中,做的是對他們的賦值,這樣是不被允許的。
2.效率要求這樣做:
類對象的構造順序顯示,進入建構函式體後,進行的是計算,是對他們的賦值操作,顯然,賦值和初始化是不同的,這樣就體現出了效率差異,如果不用成員初始化類表,那麼類對自己的類成員分別進行的是一次隱式的預設建構函式的調用,和一次複製操作符的調用,如果是類對象,這樣做效率就得不到保障。
注意:建構函式需要初始化的資料成員,不論是否顯示的出現在建構函式的成員初始化列表中,都會在該處完成初始化,並且初始化的順序和其在聲明時的順序是一致的,與列表的先後順序無關 ,所以要特別注意,保證兩者順序一致才能真正保證其效率。
為了說明清楚,假設有這樣一個類:
複製代碼 代碼如下:
class foo{
private :
int a, b;
};
1、foo(){}和foo(int i = 0){}都被認為是預設建構函式,因為後者是預設參數。兩者不能同時出現。
2、建構函式列表的初始化方式不是按照列表的的順序,而是按照變數聲明的順序。比如foo裡面,a在b之前,那麼會先構造a再構造b。所以無論 foo():a(b + 1), b(2){}還是foo():b(2),a(b+1){}都不會讓a得到期望的值。如果先聲明b再聲明a則會更好。
3、建構函式列表能夠對const成員初始化。比如foo裡面有一個int const c;則foo(int x) : c(x){}可以讓c值賦成x。不過需要注意的是,c必須在每個建構函式(如果有多個)都有值。
4、在繼承裡面,只有初始化列表可以構造父類的private成員。比如說
複製代碼 代碼如下:
class child : public foo{
}
foo裡面的建構函式是這樣寫的:foo (int x) { a = x; }.
而在child裡面寫child(int x){ foo(x); }是通過不了編譯的。只有把父類初始化改為foo(int x) : a(x){}而子類構造寫作child (int x) : foo(x){}才可以。
C++ 初始化類的成員,不但可以用建構函式(constructor)完成,而且可以用初始化類成員列表來完成。MFC大量用到此方法。例如有些初學者可能不大理解如下代碼:
class A
{
public:
int member_var; //成員變數
A(); //建構函式
}
A::A():member_var(0)
{
}
他們覺得這個建構函式的定義應該只能這樣寫:
A::A()
{
member_var=1;
}
其實兩種方法都可。但是有些情況下,只能用第一種,而且通常情況下用第一種也會效率高些。
其實,第一種方法是真正的初始化(initialization ),而在建構函式內實現的“=”操作其實是賦值(assign)。這兩種方法的一切區別從這兒開始。區別大概如下:
1.我們知道普通變數編譯器都會預設的替你初始化。他們既能初始化,也能被賦值的,而常量(const)按照其意思只能被初始化,不能賦值。否則與變數就無區別了。所以常量成員(const member)只能用成員初始化列表來完成他們的“初始化”,而不能在建構函式內為他們“賦值”。
2.我們知道類的對象的初始化其實就是調用他的建構函式完成,如果沒有寫建構函式,編譯器會為你預設產生一個。如果你自訂了帶參數的建構函式,那麼編譯器將不產生預設建構函式。這樣這個類的對象的初始化必須有參數。如果這樣的類的對象來做另外某個類的成員,那麼為了初始化這個成員,你必須為這個類的對象的建構函式傳遞一個參數。同樣,如果你在包含它的這個類的建構函式裡用“=”,其實是為這個對象“賦值”而非“初始化”它。所以一個類裡的所有建構函式都是有參數的,那麼這樣的類如果做為別的類的成員變數,你必須顯式的初始化它,你也是只能通過成員初始化列表來完成初始化。 例如:
複製代碼 代碼如下:
class B
{
......
}
class A
{
public:
B member_b;
A();
}
A::A():B(...) //你必須顯式初始化它,因為他的所有建構函式
//都是有參數的,之後才能被賦值。
{
B=...; //因為如上所寫,已經初始化了,才能被賦值,否則錯誤。
}
初始化順序:
複製代碼 代碼如下:
class test
{
const int a;
std:string str;
object o;
test():str(“df”),o(null) ,a(0)
{
}
};
黃色的既是初始化列表,他們會在建構函式正式調用前被調用,且他們的初始化順序並不是根據 初始化列表中出現的順序,而是他們聲明的順序來初始化。如上:
初始化順序是: a, str, o;
一般用於初始化 常量類型,靜態類型的資料,或者不能獨立存在的資料