In C++, why can't a derived class directly and explicitly call the destructor of its base class without using a scope resolution operator?
I've heard that in C++, all destructors are processed by the compiler into "destructor," meaning that the destructors of the base and derived classes have the same name, leading to redefinition (hiding).
Is this correct?
#include <iostream>
struct base {
base() { std::cout << "base default \n"; }
~base() { std::cout << "~base \n"; }
};
struct derived : base {
derived() { std::cout << "derived default \n"; }
~derived()
{
//~base();No
base::~base();
std::cout << "~derived \n";
}
};
I know that in practice there is almost no need to do this, I just want to understand the principle...
Read the error message. It is rather informative:
<source>: In destructor 'derived::~derived()':
<source>:12:9: error: no match for 'operator~' (operand type is 'base')
12 | ~base();
| ^~~~~~~
The inheritance is not that relevant, you'd get the same error for:
struct base {
void foo() {
~base();
}
};
If base
had an operator~
defined, that line would create a temporary base
object and call the operator. I suppose nobody really bothers about the ambiguity being resolved like that, because calling the destructor manually is really rare. Note also (from cppreference):
Calling a destructor directly for an ordinary object, such as a local variable, invokes undefined behavior when the destructor is called again, at the end of scope.
As far as I can tell, the only case when it's ok to call the destructor explicitly is when you use placement-new
, which is rather advanced and not an everyday tool. And even then, you'd want to call the correct destructor for the object rather than only the destructor of one base class.
Moreover, similar as creating an object is not just a call to the constructor, an object is not properly destroyed by merely invoking the destructor. If you manually call the destructor, you also have to manually deallocate the memory.
Phrased differently, if you want to do it all manually, you have to do it properly or not at all.
To call the base destructor, write:
this->~base();
If you do that in your code, creating a derived
and destroying it will invoke undefined behavior, because the base class destructor is already called, you don't have to do that manually.