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

Omn*_*ous 20 c++ assembly name-mangling c++17

objdump我的.o文件的显示,我对同一类两种不同的析构函数.为什么?

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>
Run Code Online (Sandbox Code Playgroud)

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

#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;
};
Run Code Online (Sandbox Code Playgroud)

AnT*_*AnT 29

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

operator delete从析构函数内部调用的需要源于C++规范,该规范指出operator delete应该选择正确的"好像"它是从最派生对象的(可能是虚拟的)析构函数中查找的.因此,operator delete可以作为静态成员函数实现的行为就像它是一个函数一样.

大多数实现"按字面意思"实现这个要求:它们不仅operator delete从析构函数中查找正确的,它们实际上从那里调用它.

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


小智 27

GCC遵循Itanium ABI:

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

安腾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
Run Code Online (Sandbox Code Playgroud)

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

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

虚拟析构函数的条目实际上是条目对.第一个析构函数,称为完整对象析构函数,在不调用对象的delete()的情况下执行销毁.第二个析构函数,称为删除析构函数,在销毁对象后调用delete().两者都破坏任何虚拟基地; 一个单独的非虚函数,称为基础对象析构函数,执行对象的销毁但不执行其虚拟基础子对象,并且不调用delete()

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

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