编译器肯定会为没有constructor的类生成一..
2012-01-24 07:57
204 查看
看完了《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个条款)
得出的结果将会是: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和国内教科书作家的的差异。。
“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和国内教科书作家的的差异。。
相关文章推荐
- 编译器肯定会为没有constructor的类生成一个default constructor吗? ——感叹国内教科书中的常识性错误 推荐
- 当没有编写时,编译器一定会生成拷贝构造函数,赋值函数 吗?
- 条款06 若不想使用编译器自动生成的函数 就该明确拒绝
- VS中没有为此解决方案配置选中要生成的项目
- 《Effective C++》学习笔记条款06 若不想使用编译器自动生成的函数,就该明确拒绝
- layout下面新建xml,但是R.java中没有对应生成相应的id
- Effective C++ 条款6——若不想使用编译器自动生成的函数,就该明确拒绝
- C#开发之问题汇总-降低NetFramework版本后,没有报错,生成失败
- 混合模式程序集是针对“V2.050727”版本生成的,在没有配置信息情况下,无发在4.0运行时架子程序集。
- 解决ffmpeg编译后ffplay没有生成的问题
- 随机生成一个没有 " - " 的uuid
- 条款6:若不想使用编译器自动生成函数,就该明确拒绝
- Android中R.java没有自动生成问题
- Android中R.java没有自动生成解决方案
- <Effective C++>:Item 6 :明确拒绝不想编译器自动生成的函数
- Linux下GCC编译器生成静态库和动态库
- Hibernate5.2.10创建表没有生成外键
- ArcGIS中使用工具后,没有反应,没有结果文件生成的问题解决办法。
- 避免QuartusII中将没有进行定义的信号自动生成wrie类型
- hibernate.hbm2ddl.auto设置为update时数据库没有自动生成表结构