编译器肯定会为没有constructor的类生成一..

 

 

看完了《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个条款)

 

    #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; 
    } 

得出的结果将会是: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和国内教科书作家的的差异。。

posted on 2012-01-24 07:57  酋长Clement  阅读(277)  评论(0编辑  收藏  举报

导航