有没有在C ++中一个中债隐含评级的默认构造函数

从汇编看c&&中默认构造函数的使用分析
c++中,如果为一个类没有明确定义一个构造函数,那么,编译器就会自动合成一个默认的构造函数。下面,通过汇编程序,来看一下其真实情况
c++中的源程序:
int main() {
上面的类X没有定义构造函数,仅仅有一个int i。
下面为其汇编程序:
: int main() {
ebp为一个寄存器,总是指向一个函数调用堆栈的栈底,作为基址,用偏移量来访问该调用栈上的变量,但这里没有任何变量要访问,因此不起作用
ebp,这两句的作用是为了保存调用main之前堆栈的基址ebp的值,并将ebp指向main调用栈的栈底
将寄存器ecx的值压栈, 栈顶指针esp向前移动4byte
这句的作用,为即将要创建的对象预留了4byte的空间,并向里面写入ecx的值
eax,eax也是一个寄存器,这里不起作用
esp,将栈顶指针移动到push ecx前的位置,即释放了4byte的空间
恢复基址到main调用之前的状态
0;函数返回
通过汇编发现,通过push ecx,编译器将堆栈栈顶移动4byte,并将寄存器的ecx的值写入,类X只含有一个int,大小刚好为4byte,因此这一句可以看成是为对象x分配空间。而接下来并没有任何函数的调用,来对这一块区域进行适当的初始化。所以,在没有明确定义一个构造函数的时候,不会有任何的初始化操作。
下面再看一段c++程序:
//增加一个成员变量int j
int main() {
与上面相比,在类X里面增加了一个成员变量int j,类的大小变为8字节。
下面为对应汇编码:
: int main() {
esp, 8; 栈顶指针移动8byte,刚好等于类X的大小
从汇编码看出,通过sub esp,8指令,堆栈确实留出了8byte的空间,刚好等于类X的大小,同样没有调用任何函数,来进行初始化操作。
所以,综上所述,在一个类没有明确定义构造函数的时候,编译器不会有任何的函数调用来进行初始化操作,仅仅是移动栈顶留出对象所需空间,也就是说,这种情况下,编译器根本不会提供默认的构造函数。
那么,书上说的由编译器提供默认的构造函数到底是怎么一回事呢?
下面看第一种情况,类里面有虚成员函数:
c++源码如下:
//增加一个成员变量int j
virtual ~X() {
int main() {
析构函数为虚函数
下面是main函数对应的汇编码:
: int main() {
esp, 12 为对象x预留12byte的空间,成员变量int i,int j占8byte,由于有虚函数,因此vptr指针占4byte
ecx, DWORD PTR _x$[ebp];获取x对象的首地址,存入ecx寄存器
??0X@@QAE@XZ;这里调用x的构造函数
ecx, DWORD PTR _x$[ebp];获取对象x的首地址
??1X@@UAE@XZ 调用析构函数
可以看到,对象x的构造函数被调用了,编译器确实合成了默认的构造函数。
下面是构造函数的汇编码:
??0X@@QAE@XZ PROC X::X, COMDAT
; _this$ = ecx
DWORD PTR _this$[ebp],ecx寄存器存有对象x的首地址
eax, DWORD PTR _this$[ebp];将对象x的首地址给寄存器eax
DWORD PTR [eax], OFFSET ??_7X@@6B@;这里设置vptr指针的值,指向vtable (OFFSET ??_7X@@6B@是获得vtable的地址)
并且通过这句,也可以证明vptr指针位于对象其实地址处
eax, DWORD PTR _this$[ebp]
可以看到,由于有虚函数,涉及到多态,因此构函数初始化了vptr指针,但是没有为另外两个变量int i,int j赋值。
从上面可以看出,类里面含有虚函数时,在没有明确定义构造函数时,编译器确实会为我们提供一个默认的构造函数。因此当一个类继承自虚基类时,也满足上面的情形。
接下来是第二种情形,类Y继承自类X,X明确定义了一个默认的构造函数(并非编译器提供),而类Y不定义任何构造函数:
先来看看c++源码:
X() {//X显示定义的默认构造函数
class Y : public X{//Y继承自X
int main() {
类Y里面没有显示定义任何构造函数
下面是main函数对应的汇编码:
: int main() {
esp, 12 为对象y预留12byte空间,y自身成员变量int i占4byte 父类中的成员变量int i int j占8byte
ecx, DWORD PTR _y$[ebp];获取对象y的首地址,存入寄存器ecx
??0Y@@QAE@XZ;调用对象y的构造函数
main函数中调用了由编译器提供的默认y对象的默认构造函数。
下面是编译器提供的y对象默认构造函数的汇编码:
??0Y@@QAE@XZ PROC Y::Y, COMDAT
; _this$ = ecx
DWORD PTR _this$[ebp],ecx中存有对象y的首地址
ecx, DWORD PTR _this$[ebp]
??0X@@QAE@XZ 调用父类X的构造函数
eax, DWORD PTR _this$[ebp]
??0Y@@QAE@XZ ENDP
可以看到y对象的构造函数又调用了父类的构造函数来初始化继承自父类的成员变量,但自身成员变量依然没有初始化。
下面是父类X的构造函数汇编码:
DWORD PTR _this$[ebp], ecx中存有对象y的首地址
eax, DWORD PTR _this$[ebp];对象y首地址给寄存器eax
DWORD PTR [eax], 0;初始化父类中的变量i
ecx, DWORD PTR _this$[ebp];对象y首地址给寄存器ecx
DWORD PTR [ecx+4], 1;初始化父类中的变量j,在对象y的内存空间中,从首地址开始的8比特用来存储继承自父对象的成员变量,后4byte用来存储自己的成员变量
由于首地址存储了父类成员变量i,因此内存地址要从对象y的首地址要移动4byte,才能找到父类成员变量j所处位置
eax, DWORD PTR _this$[ebp]
可以看到,y对象继承自父类的成员变量由父类构造函数初始化。父对象包含在子对象中,并且this指针,即寄存器ecx存储的首地址始终是子对象y的首地址。
如果父类X中也没有定义任何构造函数会怎样?
下面是c++源码:
class Y : public X{//Y继承自X
int main() {
父类和子类都没有任何构造函数。
下面是main函数汇编码:
: int main() {
esp, 12 和刚才一样,为对象y预留12byte
可以看到main中根本没有任何函数的调用,也就是说,编译器没有为子对象y提供默认构造函数。
那么,要是父类中带参数的构造函数,而子类中没有构造函数呢?这时候编译器会报错。
下面看第三种情况,类Y中包含成员对象X,成员对象有显示定义的默认构造函数,而类Y没有任何构造函数:
先看c++源码:
: int main() {
esp, 12 和刚才一样,为对象y预留12byte
类X为类Y的成员对象
下面是main函数的汇编码:
: int main() {
esp, 12 为对象y预留12byte 成员对象的变量占8byte 对象y自身占变量占4byte 成员对象包含在对象y中
ecx, DWORD PTR _y$[ebp];对象y的首地址存入ecx
??0Y@@QAE@XZ;调用对象y的构造函数,由编译器提供的默认构造函数
对象y的构造函数被调用,即编译器提供了默认的构造函数
对象y的构造函数汇编码:
??0Y@@QAE@XZ PROC Y::Y, COMDAT
; _this$ = ecx
DWORD PTR _this$[ebp],ecx中存有对象y的首地址
ecx, DWORD PTR _this$[ebp]
ecx, 4;加4是因为对象y首地址起始处存储的是自身成员变量i
??0X@@QAE@XZ 调用成员对象x的构造函数
eax, DWORD PTR _this$[ebp]
对象y的构造函数调用了成员对象x的构造函数,用来初始化成员对象中的成员变量,对象y自身的成员变量没有初始化。
成员对象x的构造函数汇编码:
??0X@@QAE@XZ PROC X::X, COMDAT
; _this$ = ecx
DWORD PTR _this$[ebp],ecx中存有成员对象x的起始地址
eax, DWORD PTR _this$[ebp];成员对象x的起始地址给eax寄存器
DWORD PTR [eax], 0;初始化成员对象x中额成员变量i
ecx, DWORD PTR _this$[ebp];成员对象x的起始地址给ecx寄存器
DWORD PTR [ecx+4], 0;初始化成员对象x中额成员变量j 加4的原因是j的地址偏离了成员对象x起始地址4byte(即成员对象x的成员变量i的字节数)
eax, DWORD PTR _this$[ebp]
但是,如果成员对象x也没有任何构造函数,情形会怎样呢?
下面是c++源码:
X//x成员对象
int main() {
下面是main函数汇编码:
: int main() {
esp, 12 为对象预留12byte空间
可以看到,main函数里面没有任何函数调用,也就是说编译器没有提供默认构造函数。
那要是成员对象x有带参数的构造函数(即非默认构造函数),而对象y没有任何构造函数呢?此时,编译器会报错。
这种情形和前一种情形很相似。
综合以上的情况,可以总结出,对于一个类不含任何构造函数,而编译器会提供默认的构造函数,有一下3种情形:
1 类本身函数虚成员函数或者继承自虚基类
2 类的基类有构造函数,并且基类构造函数还是显示定义的默认构造函数(非编译器提供),若基类的构造函数带有参数(即非默认构造函数),编译器报错
这种情况和上一种相似,类的成员对象有构造函数,并且成员对象的构造函数还是显示定义的默认构造函数(非编译器提供);若成员对象的构造函数带有参数(即非默认构造函数),编译器报错。
以上参考了《VC++深入详解》里面的知识点,还有自己的分析,欢迎指正
Copyright (C) , All Rights Reserved.
版权所有 闽ICP备号
processed in 0.052 (s). 12 q(s)对C++默认构造函数的一点重要说明
投稿:jingxian
字体:[ ] 类型:转载 时间:
下面小编就为大家带来一篇对C++默认构造函数的一点重要说明。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
大多数C++书籍都说在我们没有自己定义构造函数的时候,编译器会自动生成默认构造函数。其实这句话我一直也是
深信不疑。但是最近看了一些资料让我有了一点新的认识。
其实我觉得大多数C++书籍之所以这样描述其实是玩了文字游戏的。如果说编译器自动产生的默认构造函数对于我们
的类没有任何作用,也就是说在编译器默认生成的这个构造函数里根本没有任何实质性的代码工作,那么这种默认构
造其实是可有可无的,所以不妨说编译器其实是为每个类生成了默认构造函数的。
在深度探索C++对象模型中讲了四种关于编译器自动生成默认构造函数的情况,在这四种情况下生成的默认构
造函数里面是由实质的隐含代码操作的:
1、 如果类A成员中含有某个类B的对象,恰好类B也显示定义了构造函数,那么在产生类A对象的时候编译器会产生一个默认构造函数,在这个默认构造函数中提供了调用类A构造函数的代码。
2、如果类B继承于类A,且类A显示定义了构造函数,那么在生成类B对象的过程中编译器同样会产生一个默认构造函数,在这个构造函数中提供调用基类A构造函数的代码。
3、如果某个类含有虚函数,那么编译器会自动产生一个默认构造函数以提供虚表指针相关的初始化操作。
4、如果一个类虚继承于其他类,那么同样的编译器会为该类产生默认的构造函数。
除以上四种情况,编译器都不产生默认构造函数,因为就算编译器产生了默认构造函数,然而在该默认构造函数中没有实质的内容,那么这个默认构造函数也就是没存在的意义的,那么我们说编译器不产生也无妨吧。
以上就是小编为大家带来的对C++默认构造函数的一点重要说明全部内容了,希望大家多多支持脚本之家~
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具}

我要回帖

更多关于 中债隐含评级 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信