c++ assembly c++17 name-mangling

c++ - ¿Por qué tengo dos implementaciones de destructor en mi salida de ensamblaje?



assembly c++17 (2)

Muchos compiladores generan dos destructores diferentes para una clase: uno para destruir objetos asignados dinámicamente, otro para destruir objetos no dinámicos (objetos estáticos, objetos locales, subobjetos base o subobjetos miembros). El operator delete llamadas anterior operator delete desde dentro, el segundo no lo hace. Algunos compiladores lo hacen agregando un parámetro oculto a un destructor (las versiones anteriores de GCC lo hacen de esa manera, MSVC ++ lo hace de esa manera), algunos compiladores simplemente generan dos destructores separados (las versiones más recientes de GCC lo hacen de esa manera).

La necesidad de llamar al operator delete desde el interior del destructor surge de la especificación de C ++, que dice que la operator delete correcta del operator delete debe elegirse "como si" se buscara desde el destructor (posiblemente virtual) del objeto más derivado. Por lo tanto, la operator delete , que se puede implementar como una función miembro estática , debería comportarse como si fuera una función virtual .

La mayoría de las implementaciones implementan este requisito "literalmente": no solo buscan la operator delete correcta del operator delete desde el interior del destructor, sino que la llaman desde allí.

Por supuesto, la operator delete solo debe llamarse desde el destructor del objeto más derivado, y solo si ese objeto se asignó dinámicamente. Aquí es donde ese parámetro oculto (o dos versiones de destructor) entran en la imagen.

Esta pregunta ya tiene una respuesta aquí:

Y el objeto de mi archivo .o revela que tengo dos destructores diferentes para la misma clase. ¿Por qué?

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>

Estas son las clases en el archivo de encabezado que resultan en la generación de este código:

#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; };


GCC sigue el Itanium ABI :

A partir de GCC 3.2, las convenciones binarias de GCC para C ++ se basan en un ABI de C ++ escrito y neutral para el vendedor que fue diseñado para ser específico de Itanium de 64 bits ...

El Itanium ABI especifica diferentes destructores:

<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

La convención del número se puede ver en la salida de su ensamblaje (la diferencia entre el nombre de las dos funciones es 0 y 1).

Finalmente, aquí hay una descripción de la diferencia entre estos dos destructores:

Las entradas para destructores virtuales son en realidad pares de entradas. El primer destructor, llamado el destructor de objetos completo, realiza la destrucción sin llamar a delete () en el objeto. El segundo destructor, llamado el destructor de eliminación, llama a delete () después de destruir el objeto. Ambos destruyen cualquier base virtual; una función separada, no virtual, llamada destructor de objetos base, realiza la destrucción del objeto pero no sus subobjetos de base virtual, y no llama a delete ()

Además, esto solo sucede si tu clase tiene un destructor virtual:

Este ABI no requiere la generación o el uso de la asignación de constructores o la eliminación de destructores para clases sin un destructor virtual. Sin embargo, si una implementación emite tales funciones, debe usar los nombres externos especificados en este ABI. Si dicha función tiene un enlace externo, debe ser emitida donde sea que se haga referencia, en un grupo COMDAT cuyo nombre es el nombre externo de la función.