C++构造析构调用顺序分析

我记得我在学校的时候,以及当初看学习视频的时候,总是告诉C++对象构造的顺序,先调用父类的构造函数,后按照顺序调用各成员的构造函数,最后再调用当前类型的构造函数。反过来析构的时候,先调用当前类型析构函数,然后从下到上调用成员的析构函数,最后调用父类。

当初乍一听,并且在每个类的构造器和析构器中加打印,感觉老师说的没有问题!

但是我当时有一个疑问,C++编译器是如何控制这一套流程的呢?后来我发现老师说的并非全对。

看下面的例子:

普通构造,析构分析, 零优化的汇编代码,栈上创建对象的情况

#include <cstdio>

class Data {
 public:
  Data() { num = 10; }
  ~Data() {}

private:
  int num = 0;
};

class Data1 {
 public:
  Data1() { num = 10; }
  ~Data1() {}

private:
  int num = 0;
};

class Base {
 public:
   Base(char ch) : c{ch} {}
  ~Base() {}

 private:
      int num = 20;
      char c = 'c';
};

class Derived : Base {
 public:
  Derived() : Base('a') { 
    ::printf("hello world\n");
  }
  ~Derived() {
    ::printf("hello world again\n");
  }

  Data data_;
  Data1 data2_;
};

int main() {
  Derived d;
  return 0;
}

Derived继承了Base类(暂时不考虑写虚析构的问题),并有两个数据成员,他们的顺序为Data,然后Data1,在类Derived的构造器中,有一侧打印。

编译环境是gcc x86-64 经过编译器生成汇编代码:

Data::Data() [base object constructor]:
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        mov     rax, QWORD PTR [rbp-8]
        mov     DWORD PTR [rax], 0
        mov     rax, QWORD PTR [rbp-8]
        mov     DWORD PTR [rax], 10
        nop
        pop     rbp
        ret
Data::~Data() [base object destructor]:
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        nop
        pop     rbp
        ret
Data1::Data1() [base object constructor]:
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        mov     rax, QWORD PTR [rbp-8]
        mov     DWORD PTR [rax], 0
        mov     rax, QWORD PTR [rbp-8]
        mov     DWORD PTR [rax], 10
        nop
        pop     rbp
        ret
Data1::~Data1() [base object destructor]:
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        nop
        pop     rbp
        ret
Base::Base(char) [base object constructor]:
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        mov     eax, esi
        mov     BYTE PTR [rbp-12], al
        mov     rax, QWORD PTR [rbp-8]
        mov     DWORD PTR [rax], 20
        mov     rax, QWORD PTR [rbp-8]
        movzx   edx, BYTE PTR [rbp-12]
        mov     BYTE PTR [rax+4], dl
        nop
        pop     rbp
        ret
Base::~Base() [base object destructor]:
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        nop
        pop     rbp
        ret
.LC0:
        .string "hello world"
Derived::Derived() [base object constructor]:
        push    rbp
        mov     rbp, rsp
        push    rbx
        sub     rsp, 24
        mov     QWORD PTR [rbp-24], rdi
        mov     rax, QWORD PTR [rbp-24]
        mov     esi, 97
        mov     rdi, rax
        call    Base::Base(char) [base object constructor]
        mov     rax, QWORD PTR [rbp-24]
        add     rax, 8
        mov     rdi, rax
        call    Data::Data() [complete object constructor]
        mov     rax, QWORD PTR [rbp-24]
        add     rax, 12
        mov     rdi, rax
        call    Data1::Data1() [complete object constructor]
        mov     edi, OFFSET FLAT:.LC0
        call    puts
        jmp     .L10
        mov     rbx, rax
        mov     rax, QWORD PTR [rbp-24]
        add     rax, 12
        mov     rdi, rax
        call    Data1::~Data1() [complete object destructor]
        mov     rax, QWORD PTR [rbp-24]
        add     rax, 8
        mov     rdi, rax
        call    Data::~Data() [complete object destructor]
        mov     rax, QWORD PTR [rbp-24]
        mov     rdi, rax
        call    Base::~Base() [base object destructor]
        mov     rax, rbx
        mov     rdi, rax
        call    _Unwind_Resume
.L10:
        mov     rbx, QWORD PTR [rbp-8]
        leave
        ret
.LC1:
        .string "hello world again"
Derived::~Derived() [base object destructor]:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     QWORD PTR [rbp-8], rdi
        mov     edi, OFFSET FLAT:.LC1
        call    puts
        mov     rax, QWORD PTR [rbp-8]
        add     rax, 12
        mov     rdi, rax
        call    Data1::~Data1() [complete object destructor]
        mov     rax, QWORD PTR [rbp-8]
        add     rax, 8
        mov     rdi, rax
        call    Data::~Data() [complete object destructor]
        mov     rax, QWORD PTR [rbp-8]
        mov     rdi, rax
        call    Base::~Base() [base object destructor]
        nop
        leave
        ret
main:
        push    rbp
        mov     rbp, rsp
        push    rbx
        sub     rsp, 24
        lea     rax, [rbp-32]
        mov     rdi, rax
        call    Derived::Derived() [complete object constructor]
        mov     ebx, 0
        lea     rax, [rbp-32]
        mov     rdi, rax
        call    Derived::~Derived() [complete object destructor]
        mov     eax, ebx
        mov     rbx, QWORD PTR [rbp-8]
        leave
        ret

分析主函数和Derived类的构造函数:

main:
        push    rbp
        mov     rbp, rsp
        push    rbx
        sub     rsp, 24
        lea     rax, [rbp-32]
        mov     rdi, rax
        call    Derived::Derived() [complete object constructor]
        mov     ebx, 0
        lea     rax, [rbp-32]
        mov     rdi, rax
        call    Derived::~Derived() [complete object destructor]
        mov     eax, ebx
        add     rsp, 24
        pop     rbx
        pop     rbp
        ret
.LC0:
        .string "hello world"
Derived::Derived() [base object constructor]:
        push    rbp
        mov     rbp, rsp
        push    rbx
        sub     rsp, 24
        mov     QWORD PTR [rbp-24], rdi
        mov     rax, QWORD PTR [rbp-24]
        mov     esi, 97
        mov     rdi, rax
        call    Base::Base(char) [base object constructor]
        mov     rax, QWORD PTR [rbp-24]
        add     rax, 8
        mov     rdi, rax
        call    Data::Data() [complete object constructor]
        mov     rax, QWORD PTR [rbp-24]
        add     rax, 12
        mov     rdi, rax
        call    Data1::Data1() [complete object constructor]
        mov     edi, OFFSET FLAT:.LC0
        call    puts
        jmp     .L13
        mov     rbx, rax
        mov     rax, QWORD PTR [rbp-24]
        add     rax, 12
        mov     rdi, rax
        call    Data1::~Data1() [complete object destructor]
        mov     rax, QWORD PTR [rbp-24]
        add     rax, 8
        mov     rdi, rax
        call    Data::~Data() [complete object destructor]
        mov     rax, QWORD PTR [rbp-24]
        mov     rdi, rax
        call    Base::~Base() [base object destructor]
        mov     rax, rbx
        mov     rdi, rax
        call    _Unwind_Resume

可以看到主函数内,构造Derived对象调用了Derived的构造函数,然后Derived的构造函数内部首先会调用Base,其次按顺序调用DataData1的构造函数,最后调用::printf打印,从形式上:"Base->Data->Data1->Derived"的顺序而已,析构函数亦然。

或许你也可以打印一个调用栈看看 嘿嘿。我老师教的,还有网上乱教的 都是误人子弟

#include <execinfo.h>
#include <unistd.h>

#include <string>

std::string stackTrace() {
  const int max_frames = 15;
  void *frame[max_frames]{};
  int nptrs = ::backtrace(frame, max_frames);
  char **strings = ::backtrace_symbols(frame, nptrs);
  if (not strings) {
    return {};
  }

  std::string stack;
  for (int i = 1; i < nptrs; ++i) {
    if (nullptr == strings[i]) break;
    stack += strings[i];
    stack += "\n";
  }

  return stack;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

歪锅锅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值