C++初始化列表


原文:https://zhuanlan.zhihu.com/p/33004628

下面我們先看例子

 1 #include <iostream>
 2 using namespace std;
 3 class Base
 4 {
 5 public:
 6     Base(int val)
 7    {
 8        m_num = 0;
 9         cout << "create Base(int val)" << endl;
10     }
11 private:
12     int m_num;
13 };

 

上邊的代碼,我先定義了一個Base類,並且定義了有一個整型實參的構造函數Base(int val)

 

 1 class BaseChild: public Base
 2 {
 3 public:
 4     BaseChild()
 5     {
 6         m_num = 0;
 7         cout << "create is BaseChild()" << endl;
 8     }
 9 
10 private:
11     int m_num;
12 };
13 
14 int main(int argc, char *argv[])
15 {
16     BaseChild child;
17 }

 

上邊的代碼繼承Base,定義了它的默認構造函數

並且在主函數中創建BaseChild的對象child

編譯但報如下錯誤:

 

 

這意思是說,沒有Base的默認構造函數。

 

結論1:如果沒有定義任何構造函數,C++編譯器會自動創建一個默認構造函數。
結論2:如果已經定義了一個構造函數,編譯器不會自動創建默認構造函數,只能顯式調用該構造函數。

 

在C++中,當創建一個對象時,編譯器要保證調用了所有子對象的構造函數,這是C++強制要求的,也是它的一個機制。

 

因為在Base中沒有定義默認構造函數,只定義了一個有整型參數的構造函數,因此編譯器並不會再去生成一個默認的構造函數,而BaseChild繼承Base時,又沒有顯式地指定Base的構造函數,所以編譯報錯。

 

如果我們不修改Base,那么,我們用什么辦法不去調用默認構造函數,而是顯式的調用Base帶參構造函數呢。答案就是初始化列表。

 

C++就為我們提供了這樣的語法。即在冒號和這個構造函數定義體的左括號之間可指定基類構造函數,如下:

1 BaseChild():Base(1)
2 {
3     cout << "create is BaseChild()" << endl;
4 }

 

現在,再編譯程序,輕松通過。

 

當然,初始化列表還可以對類本身的數據成員進行初始化,如對BaseChild成員m_num進行初始化:

 1 BaseChild():Base(1), m_num(0){...}  

中間要以逗號隔開。

 

細心的同學,可能會提問,我們平常見到的都是

int m_num = 0;

而剛剛的代碼是m_num(0),這是正確的,我們可以認為這就是調用了int類型的構造函數。類似的,new int(2)是一樣的道理。

 

上面是整數類型的賦值,那么,如果是對象之間的賦值呢,例如:

BaseChild child = BaseChild();

其實,這又涉及了另外一個話題,賦值構造函數和編譯器的優化。

其具體執行順序是:

1調用BaseChild構造函數,生成一個臨時對象

2給child成員賦值

3創建child對象后,刪除臨時對象

那么,針對上面的順序,編譯器有可能會優化代碼為BaseChild child()直接創建child對象。

 

最后,總結一下初始化列表吧:

1 因為初始化列表中無法直接初始化基類的數據成員,所以你需要在列表中指定基類的構造函數,如果不指定,編譯器則會調用基類的默認構造函數。

 

2 推薦使用初始化列表,它會比在函數體內初始化派生類成員更快,這是因為在分配內存后,在函數體內又多進行了一次賦值操作。

 

3 初始化列表並不能指定初始化的順序,正確的順序是,首先初始化基類,其次根據派生類成員聲明次序依次初始化。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM