And objdump
of my .o file reveals that I have two different destructors for the same class. Why?
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>
These are the classes in the header file that result in this code being generated:
#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;
};
Many compilers generate two different destructors for one class: one for destroying dynamically allocated objects, another - for destroying non-dynamic objects (static objects, local objects, base sub-objects or member sub-objects). The former calls operator delete
from inside, the latter doesn't. Some compilers do it by adding a hidden parameter to one destructor (older versions of GCC do it that way, MSVC++ does it that way), some compilers simply generate two separate destructors (newer versions of GCC do it that way).
The need to call operator delete
from inside the destructor arises from C++ specification, which says that the proper operator delete
should be chosen "as if" it was looked up from inside the (possibly virtual) destructor of the most derived object. So, operator delete
, which can be implemented as a static member function should behave as if it were a virtual function.
Most implementations implement this requirement "literally": they not only look up the proper operator delete
from inside the destructor, they actually call it from there.
Of course, operator delete
only has to be called from the most derived object's destructor, and only if that object was dynamically allocated. This is where that hidden parameter (or two versions of destructor) come into the picture.
GCC follows the Itanium ABI:
Starting with GCC 3.2, GCC binary conventions for C++ are based on a written, vendor-neutral C++ ABI that was designed to be specific to 64-bit Itanium ...
The Itanium ABI specifies different destructors:
<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
The number convention can be seen in your assembly output (the difference between the name mangling in the two functions is 0 and 1).
Finally, here's a description of the difference between these two destructors:
The entries for virtual destructors are actually pairs of entries. The first destructor, called the complete object destructor, performs the destruction without calling delete() on the object. The second destructor, called the deleting destructor, calls delete() after destroying the object. Both destroy any virtual bases; a separate, non-virtual function, called the base object destructor, performs destruction of the object but not its virtual base subobjects, and does not call delete()
Further, this only happens if your class has a virtual destructor:
This ABI does not require the generation or use of allocating constructors or deleting destructors for classes without a virtual destructor. However, if an implementation emits such functions, it must use the external names specified in this ABI. If such a function has external linkage, it must be emitted wherever referenced, in a COMDAT group whose name is the external name of the function.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With