为什么我的汇编输出中有两个析构函数实现?

时间:2017-06-15 03:40:02

标签: c++ assembly c++17 name-mangling

我的.o文件的objdump显示我有两个不同的析构函数用于同一个类。为什么呢?

Disassembly of section .text._ZN1AD0Ev:

0000000000000000 <_ZN1AD0Ev>:
   0:   53                      push   %rbx
   1:   be 00 00 00 00          mov    $0x0,%esi
   6:   48 89 fb                mov    %rdi,%rbx
   9:   48 c7 07 00 00 00 00    movq   $0x0,(%rdi)
  10:   ba 2c 00 00 00          mov    $0x2c,%edx
  15:   bf 00 00 00 00          mov    $0x0,%edi
  1a:   e8 00 00 00 00          callq  1f <_ZN1AD0Ev+0x1f>
  1f:   48 89 df                mov    %rbx,%rdi
  22:   be 08 00 00 00          mov    $0x8,%esi
  27:   5b                      pop    %rbx
  28:   e9 00 00 00 00          jmpq   2d <_ZN1AD0Ev+0x2d>

Disassembly of section .text._ZN1AD2Ev:

0000000000000000 <_ZN1AD1Ev>:
   0:   48 c7 07 00 00 00 00    movq   $0x0,(%rdi)
   7:   ba 2c 00 00 00          mov    $0x2c,%edx
   c:   be 00 00 00 00          mov    $0x0,%esi
  11:   bf 00 00 00 00          mov    $0x0,%edi
  16:   e9 00 00 00 00          jmpq   1b <_ZN1AD1Ev+0x1b>

这些是头文件中导致生成此代码的类:

#include <iostream>

class A {
 public:
   virtual ~A() {
      ::std::cout << "This destructor does something significant.\n";
   }
};

class B : public A {
 public:
   inline virtual ~B() = 0;
};

B::~B() = default;

class C : public B {
 public:
   inline virtual ~C() = default;
};

2 个答案:

答案 0 :(得分:29)

许多编译器为一个类生成两个不同的析构函数:一个用于销毁动态分配的对象,另一个用于销毁非动态对象(静态对象,本地对象,基础子对象或成员子对象)。前者从内部调用operator delete,后者不调用operator delete。一些编译器通过向一个析构函数添加一个隐藏参数来做到这一点(旧版GCC就是这样做的,MSVC ++就是这样做的),一些编译器只生成两个独立的析构函数(GCC的新版本就是这样做的。)

需要从析构函数中调用operator delete来自C ++规范,该规范指出应该选择正确的operator delete&#34;好像&#34;它是从最派生对象的(可能是虚拟的)析构函数中查找的。因此,可以作为静态成员函数实现的operator delete应该表现得像虚拟函数一样。

大多数实现实现了这个要求&#34;字面意思&#34;:他们不仅从析构函数中查找正确的operator delete,他们实际上从那里调用

当然,只需从最派生对象的析构函数中调用Authorization:,并且只有在动态分配该对象时才需要调用Bearer。这是隐藏参数(或两个版本的析构函数)进入图片的地方。

答案 1 :(得分:27)

GCC follows the Itanium ABI

  

从GCC 3.2开始,C ++的GCC二进制约定基于一个专门针对64位Itanium而设计的供应商中立的C ++ ABI ...

Itanium ABI指定了不同的析构函数:

  <ctor-dtor-name> ::= C1   # complete object constructor
           ::= C2   # base object constructor
           ::= C3   # complete object allocating constructor
           ::= D0   # deleting destructor
           ::= D1   # complete object destructor
           ::= D2   # base object destructor

可以在程序集输出中看到数字约定(两个函数中名称修改的差异为0和1)。

最后,这里描述了这两个析构函数之间的区别:

  

虚拟析构函数的条目实际上是条目对。该   第一个析构函数,称为完整对象析构函数,执行   破坏而不在对象上调用delete()。第二   析构函数,称为删除析构函数,之后调用delete()   摧毁物体。两者都破坏任何虚拟基地;独立,   非虚函数,称为基础对象析构函数,执行   破坏对象但不破坏其虚拟基础子对象   不会调用delete()

此外,只有当您的类具有虚拟析构函数时才会发生这种情况:

  

此ABI不需要为没有虚拟析构函数的类生成或使用分配构造函数或删除析构函数。但是,如果实现发出此类函数,则必须使用此ABI中指定的外部名称。如果此类函数具有外部链接,则必须在COMDAT组中的任何位置引用它,该COMDAT组的名称是函数的外部名称。