窥视C++细节-编译器帮助合成默认构造器的情况


一般的C++书籍上会告诉你,当一个类没有声明任何构造函数时,编译器会自动的生成默认构造函数。但实际的情况好像不是这样的,当一个类没有声明任何构造函数,且在必要的时候编译器才会帮助合成默认构造器,在一些非必要的时候,编译是不会帮助合成默认构造器的。

下面通过把C++编译成汇编的方式,分析哪四种常见情况下,编译器会帮助合成默认构造器。

环境

https://godbolt.org/,这个网站可以方便的将C++代码编译成汇编,同时使用颜色标识某条C++语句对应的汇编代码。

在使用之前需要可以根据自己的习惯配置一下。
在这里插入图片描述

使用的gcc版本是10.2

-m32:将C++代码编译成32位的汇编

-O0:不要进行编译优化

Intel asm syntax:编译成intel格式的汇编,默认是AT&T格式的汇编。

Demangle identifiers:反命名倾轧,在汇编代码中使用C++中函数的名字,这样更便于阅读。

编译器并没有帮助合成默认构造器

在这里插入图片描述

此时类A并没有手动提供任意构造器,且编译器也没有帮助合成默认构造器。

类内数据成员是类对象

  • 该类没有手动提供任何构造器
  • 类内部有一个数据成员是类对象
  • 该类对象有默认构造函数(手动提供或编译器帮助合成的)
    在这里插入图片描述
    先看一个反例,此时类B内部虽然有类A对象a,但类A内没有默认构造函数,所以编译器并不会给类B合成默认构造器。
    在这里插入图片描述
    给类A手工提供了一个默认的构造器,此时类B内部在定义类A对象a时,需要调用类A的默认构造器。又因为类B没有默认构造器,所以编译器帮助它合成一个默认的构造器,并在这个构造器中调用了类A的默认构造器。
    在这里插入图片描述
    类B虽然没有手动提供默认构造器,但其内部数据成员类A有默认构造器,类B为了调用类A的默认构造器,所以编译器为其合成了一个默认构造器,且内部调用了类A的默认构造器。此时类B有了编译器合成的默认构造器,类B有又作为类C的数据成员,且类C内部没有手动提供默认构造器,为了调用类B的默认构造器,编译器会为类C合成一个默认构造器,并在合成的构造器中调用类B的默认构造器。

父类有默认构造器

  • 父类有默认构造器(手动提供的或编译器帮助合成的)
  • 子类内部没有任何构造器

当创建一个子类对象时,父类的默认构造器需要被调用,又因为子类中没有默认构造器,所以为了调用父类的默认构造器,编译器会为子类合成一个默认的构造器,并在其中调用父类的默认构造器。
在这里插入图片描述

类内有虚函数

  • 类内有虚函数
  • 类内没有任何构造器

因为类内有虚函数,所以这个类需要有一个指向虚函数表的指针,为了初始化这个指针,编译器会为这个类合成一个默认构造器,在这个构造器中会对类对象的虚函数表指针进行初始化,使其指向类的虚函数表。
在这里插入图片描述

A::A() [base object constructor]:
        push    ebp
        mov     ebp, esp
        mov     edx, OFFSET FLAT:vtable for A+8   #获取指向虚函数表的指针
        mov     eax, DWORD PTR [ebp+8]            #获取对象a的地址
        mov     DWORD PTR [eax], edx    #将虚函数表指针写入对象a的前四个字节
        nop
        pop     ebp
        ret
main:
        lea     ecx, [esp+4]
        and     esp, -16
        push    DWORD PTR [ecx-4]
        push    ebp
        mov     ebp, esp
        push    ecx
        sub     esp, 20
        sub     esp, 12
        lea     eax, [ebp-16]   #获取对象a的地址
        push    eax             #对象a的地址,作为A::A()调用前的参数,压栈。
        call    A::A() [complete object constructor]
        add     esp, 16
        mov     eax, 0
        mov     ecx, DWORD PTR [ebp-4]
        leave
        lea     esp, [ecx-4]
        ret

在这里插入图片描述

有虚继承

  • 类带有虚基类
  • 类内没有任何构造器

类使用了虚继承,那么需要为类初始虚基表指针,此时编译器会合成一个默认构造器,再提供相应的代码,以用于类对象内虚基表指针的初始化。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值