C++構造函數語義——默認構造函數


0、前言


  《The C++ ARM》告訴我們:“默認構造函數會在需要的時候自動生成(被編譯器)。”然后“在需要的時候”是一個很抽象的概念,本文主要描述的問題也正是這些需要的情況。

  我們看看下面的代碼片段:

[cpp] view plaincopyprint?
  1. class Foo  
  2. {    
  3. public:  
  4.     int val;  
  5.     Foo *pnext;  
  6. };  
  7. void foo_bar()  
  8. {    
  9.     Foo bar;  
  10.     if (bar.val || bar.pnext)  
  11.     {  
  12.         cout << bar.val << endl;  
  13.         cout << bar.pnext << endl;  
  14.     }  
  15. }  
用戶並沒有顯示地定義默認構造函數,編譯器會為它自動生成一個無關緊要(trivial)的默認構造函數,生成的默認構造函數什么也不錯,既不會講其成員變量置零,也不會做其他的任何事情,只是為了保證程序能夠正確運行而已,這就是所謂的“需要”,如果還需要給初始化成員變量,這件事情還是交給程序員做吧!


一、非平凡(non-trivival)默認構造函數


C++標准描述了哪些情況,這樣的隱式默認構造函數是無關緊要的。一個非平凡(non-trivival)的默認構造函數是ARM中所說的被實現所“需要”,並在必要的時候被編譯器自動生成。下面來看看默認構造函數是非平凡的四種情況:


1.1 含有包含默認構造函數的成員類對象


如果該類包含一個成員類對象,它有默認的構造函數,那么這個類的隱式構造函數是非平凡的,並且編譯器需要為包含的這個成員類對象生成一個默認構造函數。然后,這個編譯器生成的默認構造函數只有在實際上被調用時才會被真正的生成。

[cpp] view plaincopyprint?
  1. class Foo  
  2. {  
  3. public:  
  4.   
  5.     Foo(){ _i = 1; }  
  6.     int _i;  
  7. };  
  8. class Bar  
  9. {  
  10. public:  
  11.     Foo foo;  
  12.     char *str;  
  13. };  
  14. void foo_bar()  
  15. {  
  16.     Bar bar;  
  17. }  

  在這個程序片段中Bar的成員foo含有默認構造函數,它初始化自己的類成員_i為1而Bar本身並沒有定義默認的構造函數,這個構造函數的目的是為了初始化它的成員變量foo,實際上就是調用Bar::foo的默認構造函數,但它並不會做一丁點關於另外一個變量str的初始化和賦值工作,初始化Bar::foo是編譯器的責任,二初始化str是程序員的。我們可以用以下代碼來大致描述一下編譯器的工作:

[cpp] view plaincopyprint?
  1. inline   
  2. Bar::Bar()   
  3. {   
  4.    // Pseudo C++ Code   
  5.    foo.Foo::Foo();   
  6. }  

  如果這里的Bar含有默認構造函數呢?我們可以從編譯器和程序員的責任划分來考慮這個問題:

  1. Bar的默認構造函數調用foo的默認構造函數,那么編譯器啥也不用做了。

  2. Bar的默認構造函數中沒有去foo這個成員變量,那么編譯器需要去幫助程序員把這件事情做完,插入一條類似“foo.Foo::Foo();”的代碼。

注:如果Bar含有多個成員類變量,則編譯器會按照這些變量的聲明順序去做以上處理。


1.2 含有包含默認構造函數的基類


  類似的,要是一個繼承的基類包含默認構造函數而該類本身沒有任務的構造函數,那么編譯器會生成一個默認構造函數,目的是初始化它的基類。

  當程序員為該類定義了多個構造函數,就是沒定義默認構造函數呢?

  在這種情況下,編譯器會在每一個構造函數中增加(augment)有關調用基類的默認構造函數部分代碼。


1.3 含有虛函數


  生成的默認構造函數是必須的當另外兩個額外條件(滿足其一):

  1.該類定義了(或繼承了)虛函數。

  2.在該類的繼承關系中,有一個或更多的虛基類。

  可以參考一下的類繼承關系:

[cpp] view plaincopyprint?
  1. class Widget  
  2. {  
  3. public:  
  4.     virtual void flip() = 0;  
  5. };  
  6. class Bell: public Widget  
  7. {  
  8. public:  
  9.     void flip(){ cout <<"Bell." << endl; }  
  10. };  
  11. class Whistle: public Widget  
  12. {  
  13. public:  
  14.     void flip(){ cout <<"Whistle." << endl; }  
  15. };  
  16.   
  17. void flip(Widget &widget)  
  18. {  
  19.     widget.flip();  
  20. }  
  21.   
  22. void foo()  
  23. {  
  24.     Bell b;  
  25.     Whistle w;  
  26.     flip(b);  
  27.     flip(w);  
  28. }  

  在編譯時,在默認構造函數中會發生下面的兩個類擴充(augmentation):

  1.虛表會被產生,其內容被這個類的活動(active)虛函數填充。

  2.編譯器為每個類對象生成一個虛指針(vtbl)。


1.4 含有一個虛基類


  關於虛基類的內容,本文暫不詳述,需要記住的是:編譯器會為該類添加一個類似虛指針的東西——“_vbcX”。


二、參考文獻


  《Inside The C++ Object Model》


轉自:http://blog.csdn.net/woods2001/article/details/7395129


原文地址:點擊打開鏈接


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com