C++初始化列表的一些問題
阿新 • • 發佈:2021-01-28
在我們建立類物件的時候,類物件的構造順序如下:
- 分配記憶體,呼叫建構函式時,隱式/顯示的初始化各資料成員;
- 進入建構函式後在建構函式中執行一般賦值與計算。
使用初始化列表的情況有三種:
- 需要初始化的資料成員是物件的情況
- 需要初始化const修飾的類成員以引用型別的成員資料
- 子類需要初始化父類的私有成員
-
情況一的說明:資料成員是物件,並且這個物件只有含引數的建構函式,沒有無引數的建構函式;
如果我們有一個類成員,它本身是一個類或者是一個結構,而且這個成員它只有一個帶引數的建構函式,而沒有預設建構函式,這時要對這個類成員進行初始化,就必須呼叫這個類成員的帶引數的建構函式,如果沒有初始化列表,那麼他將無法完成第一步,就會報錯。
#include "iostream" using namespace std; class Test { public: Test (int, int, int){ cout <<"Test" << endl; }; private: int x; int y; int z; }; class Mytest { public: Mytest():test(1,2,3){ //初始化 cout << "Mytest" <<
輸出結果
如果沒有 mytest():test(1,2,3){}
初始化列表就會報錯
因為Test有了顯示的帶引數的建構函式,那麼他是無法依靠編譯器生成無參建構函式的,所以沒有三個int型資料,就無法建立Test的物件。Test類物件是MyTest的成員,想要初始化這個物件test,那就只能用成員初始化列表,沒有其他辦法將引數傳遞給Test類建構函式。
注意: 初始化列表在建構函式執行之前執行。
-
情況二的說明:物件引用或者cosnt修飾的資料成員
當類成員中含有一個const物件時,或者是一個引用時,他們也必須要通過成員初始化列表進行初始化,因為這兩種物件要在聲明後馬上初始化,而在建構函式中,做的是對他們的賦值,這樣是不被允許的。
-
情況三的說明:子類初始化父類的私有成員,需要在(並且也只能在)引數初始化列表中顯示呼叫父類的建構函式:
程式碼如下(參考了之前學習設計模式時候的程式碼):
#include <iostream>
#include <vector>
using namespace std;
class Context {
public:
bool isMgr;
};
class Base {
public:
Base(Base* c = nullptr){
cc = c;
}
virtual double Calc(Context& ctx) {
printf("base/n");
return 0.0;
}
virtual ~Base() {};
protected:
Base* cc;
};
class Son1 : public Base{
public:
Son1(Base* c) : Base(c) {}
~Son1() {}
virtual double Calc(Context& ctx) {
printf("son1\n");
return 0.0 + cc->Calc(ctx);
}
};
class Son2 : public Base {
public:
Son2(Base* c) : cc(c) {}
~Son2() {}
virtual double Calc(Context& ctx) {
printf("son2\n");
return 0.0 + cc->Calc(ctx);
}
protected:
Base* cc;
};
int main() {
Context ctx1;
ctx1.isMgr = true;
Base* base = new Base(nullptr);
Base* cb1 = new Son1(base);
Base* cb2 = new Son2(cb1);
cb2->Calc(ctx1);
return 0;
}
注意:
-
初始化列表在進行初始化時的順序和類內成員宣告順序相同,和初始化順序寫法沒有關係;
-
預設建構函式
foo(){}
和foo(int i = 0){}
都是預設建構函式。兩者不能同時出現 -
在繼承中,只有初始化列表可以構造父類的
private
成員(需要顯式地呼叫父類地建構函式)。class foo { private: int a; public: foo(int x){ a = x; } }; class child : public foo{ child(int x):foo(x){}; };