當(dāng)前位置:首頁文章首頁 新聞中心 業(yè)務(wù)咨訊

感嘆國內(nèi)教科書中的常識性錯(cuò)誤

作者:  來源:  發(fā)布時(shí)間:2012-2-1 8:31:32  點(diǎn)擊:
看完了《inside c++ object model》,接下來,就是親自動手實(shí)踐驗(yàn)證了。 依然記得大二時(shí)候那本清華大學(xué)出版社出版的C++教材,我用了2周時(shí)間就看完了,那時(shí)候的我還沒有任何面向?qū)ο蟮乃枷,更別提項(xiàng)目經(jīng)驗(yàn)了。感覺整個(gè)C++就是把C的struct擴(kuò)充了一下,又加了點(diǎn)多態(tài)機(jī)制和泛型編程罷了(事實(shí)上,那時(shí)候?qū)Χ鄳B(tài)和泛型編程的概念也不是很清楚,只知道virtual和templete)。呵呵。。 而今看來很是自負(fù)啊。  事實(shí)上那本書雖然簡單,但我卻看過很多遍,在講到構(gòu)造函數(shù)時(shí),書中有一條很明確的寫到:“當(dāng)一個(gè)類沒有構(gòu)造函數(shù)時(shí)候,那么編譯器就會為它生成一個(gè)默認(rèn)的構(gòu)造函數(shù)”。 在我沒有讀到Lippman大牛的著作權(quán),或許我會一直以為這是個(gè)永恒不變的真理。Lippman對于很多程序員的這一點(diǎn)認(rèn)識如是評論道:
“C++新手一般有兩個(gè)常見的誤解:
1.任何class如果沒有定義default construtor,就會被合成一個(gè)出來;
2.編譯器合成出來的default constructor會明確設(shè)定class內(nèi)每一個(gè)data member”
看看第一條,莫不是說那個(gè)寫教科書的人也是個(gè)C++新手?呵呵。。
Lippman又明確解釋道,編譯器只有在需要的時(shí)候才會自動生成一個(gè)default constructor。至于什么時(shí)候是必要的時(shí)候:符合以下4點(diǎn)就是有必要的時(shí)候:
1.帶有default constructor的成員class member object;
2.帶有default constructor的基類;
3.帶有virtual function;
4.帶有vitrual base class。
Lippman對于這四點(diǎn)沒有給出明確的驗(yàn)證實(shí)例,因?yàn)橐?yàn)證一個(gè)編譯器是否為一個(gè)類合成了default constructor,除了從匯編語言的角度來看匯編代碼外,貌似沒有其它任何方法了。。感嘆自己匯編很弱,不能從匯編角度來分析,但幸運(yùn)的是我在VS2008里面無意中發(fā)現(xiàn)了一種驗(yàn)證方法。先來看看如下代碼:(這點(diǎn)簡單代碼能驗(yàn)證上述4個(gè)條款)
 
#include <iostream> 
 
using namespace std; 
 
class BaseClass
{
public:
    int a;
    int b;
    //virtual void showClassInfo(){};
    //BaseClass(){}
}; 
 
class DerivedClass:public BaseClass
{
public:
    int c;
}; 
 
int main(int *argc,char **argv)
{
    BaseClass baseClass;
    //cout<<baseClass.a<<" "<<baseClass.b<<endl;
    cout<<sizeof(baseClass)<<endl;
    DerivedClass derivedClass;
    //cout<<derivedClass.c<<endl;
    cout<<sizeof(derivedClass)<<endl;
    return 0;
}
得出的結(jié)果將會是:8 12 
這證明類的nonstatic data member被正確的申請了內(nèi)存空間。而如果我們?nèi)サ簦?br />//cout<<baseClass.a<<" "<<baseClass.b<<endl;
這行的注釋,會出現(xiàn)runtime error. 給出的提示信息為:the variable “baseClass”is being used without being initialized.(baseClass未進(jìn)行初始化),我假設(shè)這句話意圖在指出這個(gè)異常是由于baseClass沒有設(shè)定默認(rèn)的構(gòu)造函數(shù),而并不是因?yàn)闆]有給baseClass.a和baseClass.b賦初值,為了驗(yàn)證這一點(diǎn),我加了一個(gè)什么也沒做的構(gòu)造函數(shù),去掉
//BaseClass(){}
這一行的注釋,那么得出的結(jié)果會是:
-858993460 -858993460
8
12
此時(shí)程序沒有任何異常。驗(yàn)證了我之前的假設(shè)是對的。構(gòu)造函數(shù)的確沒有給baseClass的data member做初始化操作,而卻得以正常運(yùn)行。
如果我們將上述代碼中的這一樣注釋掉:(注意BaseClass的構(gòu)造函數(shù)此時(shí)是注釋掉的)
//virtual void showClassInfo(){};
這行代碼的注釋去掉,那么程序會照樣正常運(yùn)行,得出的結(jié)果會是:
-858993460 -858993460
12
16
此時(shí)baseClass中隱含了一個(gè)vptr指向virtual function table,造就了baseClass的重新布局(增加了4個(gè)byte)。對于C++對象布局,可以看看我之前的一篇博文“點(diǎn)擊這里”  。這也就驗(yàn)證了Lippman所說的第三條。為了驗(yàn)證第四條,我們將上述代碼中的這一行:
//cout<<derivedClass.c<<endl;
注釋去掉,此時(shí)又會出現(xiàn)runtime error.而給出的信息為:the variable “derivedClass”is being used without being initialized.(derivedClass未進(jìn)行初始化)。證明編譯器又沒有為非vitual繼承的derived class合成默認(rèn)的構(gòu)造函數(shù)。 那么正常的virtual 繼承會出現(xiàn)什么狀況呢?再試著將這行代碼
class DerivedClass:public BaseClass
改為如下:
class DerivedClass:virtual public BaseClass
(注意//cout<<derivedClass.c<<endl;的注釋是去掉的)
程序又將會正常運(yùn)行,而且得出的結(jié)果是:
8
-858993460
16
如此便驗(yàn)證了第四條。
至此,第一條和第二條的驗(yàn)證方法也就沒必要再說下去了,大同小異。
最后,不得不感嘆國外教科書作家Lippman和國內(nèi)教科書作家的的差異。。

相關(guān)軟件

文章評論

軟件按字母排列: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z