程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 編譯器肯定會為沒有constructor的類生成一個default constructor嗎? ——感歎國內教科書中的常

編譯器肯定會為沒有constructor的類生成一個default constructor嗎? ——感歎國內教科書中的常

編輯:關於C語言

 

 

看完了《inside c++ object model》,接下來,就是親自動手實踐驗證了。 依然記得大二時候那本清華大學出版社出版的C++教材,我用了2周時間就看完了,那時候的我還沒有任何面向對象的思想,更別提項目經驗了。感覺整個C++就是把C的struct擴充了一下,又加了點多態機制和泛型編程罷了事實上,那時候對多態和泛型編程的概念也不是很清楚,只知道virtual和templete)。呵呵。。 而今看來很是自負啊。  事實上那本書雖然簡單,但我卻看過很多遍,在講到構造函數時,書中有一條很明確的寫到:“當一個類沒有構造函數時候,那麼編譯器就會為它生成一個默認的構造函數”。 在我沒有讀到Lippman大牛的著作權,或許我會一直以為這是個永恆不變的真理。Lippman對於很多程序員的這一點認識如是評論道:

“C++新手一般有兩個常見的誤解:

1.任何class如果沒有定義default construtor,就會被合成一個出來;

2.編譯器合成出來的default constructor會明確設定class內每一個data member”

看看第一條,莫不是說那個寫教科書的人也是個C++新手?呵呵。。 

Lippman又明確解釋道,編譯器只有在需要的時候才會自動生成一個default constructor。至於什麼時候是必要的時候:符合以下4點就是有必要的時候:

1.帶有default constructor的成員class member object;

2.帶有default constructor的基類;

3.帶有virtual function;

4.帶有vitrual base class。

Lippman對於這四點沒有給出明確的驗證實例,因為要驗證一個編譯器是否為一個類合成了default constructor,除了從匯編語言的角度來看匯編代碼外,貌似沒有其它任何方法了。。感歎自己匯編很弱,不能從匯編角度來分析,但幸運的是我在VS2008裡面無意中發現了一種驗證方法。先來看看如下代碼:這點簡單代碼能驗證上述4個條款)

 

  1. #include <iostream>  
  2.  
  3. using namespace std;  
  4.  
  5. class BaseClass 
  6. public: 
  7.     int a; 
  8.     int b; 
  9.     //virtual void showClassInfo(){}; 
  10.     //BaseClass(){} 
  11. };  
  12.  
  13. class DerivedClass:public BaseClass 
  14. public: 
  15.     int c; 
  16. };  
  17.  
  18. int main(int *argc,char **argv) 
  19.     BaseClass baseClass; 
  20.     //cout<<baseClass.a<<" "<<baseClass.b<<endl; 
  21.     cout<<sizeof(baseClass)<<endl; 
  22.     DerivedClass derivedClass; 
  23.     //cout<<derivedClass.c<<endl; 
  24.     cout<<sizeof(derivedClass)<<endl; 
  25.     return 0; 

得出的結果將會是:8 12  

這證明類的nonstatic data member被正確的申請了內存空間。而如果我們去掉:

//cout<<baseClass.a<<" "<<baseClass.b<<endl;

這行的注釋,會出現runtime error. 給出的提示信息為:the variable “baseClass”is being used without being initialized.(baseClass未進行初始化),我假設這句話意圖在指出這個異常是由於baseClass沒有設定默認的構造函數,而並不是因為沒有給baseClass.a和baseClass.b賦初值,為了驗證這一點,我加了一個什麼也沒做的構造函數,去掉

//BaseClass(){}

這一行的注釋,那麼得出的結果會是:

-858993460 -858993460
8
12

此時程序沒有任何異常。驗證了我之前的假設是對的。構造函數的確沒有給baseClass的data member做初始化操作,而卻得以正常運行。

如果我們將上述代碼中的這一樣注釋掉:注意BaseClass的構造函數此時是注釋掉的)

//virtual void showClassInfo(){};

這行代碼的注釋去掉,那麼程序會照樣正常運行,得出的結果會是:

-858993460 -858993460
12
16

此時baseClass中隱含了一個vptr指向virtual function table,造就了baseClass的重新布局增加了4個byte)。對於C++對象布局,可以看看我之前的一篇博文“點擊這裡”  。這也就驗證了Lippman所說的第三條。為了驗證第四條,我們將上述代碼中的這一行:

//cout<<derivedClass.c<<endl;

注釋去掉,此時又會出現runtime error.而給出的信息為:the variable “derivedClass”is being used without being initialized.(derivedClass未進行初始化)。證明編譯器又沒有為非vitual繼承的derived class合成默認的構造函數。 那麼正常的virtual 繼承會出現什麼狀況呢?再試著將這行代碼

class DerivedClass:public BaseClass

改為如下:

class DerivedClass:virtual public BaseClass

注意//cout<<derivedClass.c<<endl;的注釋是去掉的)

程序又將會正常運行,而且得出的結果是:

8
-858993460
16

如此便驗證了第四條。

至此,第一條和第二條的驗證方法也就沒必要再說下去了,大同小異。

最後,不得不感歎國外教科書作家Lippman和國內教科書作家的的差異。。

 

 

本文出自 “酋長 ” 博客,請務必保留此出處http://clement.blog.51cto.com/2235236/767263

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved