We once had an interview with a very experienced C++ developer who couldn't answer the following question: is it necessary to call the base class destructor from the derived class destructor in C++?
Obviously the answer is no, C++ will call the base class destructor automagically anyway. But what if we attempt to do the call? As I see it the result will depend on whether the base class destructor can be called twice without invoking erroneous behavior.
For example in this case:
class BaseSafe {
public:
~BaseSafe()
{
}
private:
int data;
};
class DerivedSafe {
public:
~DerivedSafe()
{
BaseSafe::~BaseSafe();
}
};
everything will be fine - the BaseSafe
destructor can be called twice safely and the program will run allright.
But in this case:
class BaseUnsafe {
public:
BaseUnsafe()
{
buffer = new char[100];
}
~BaseUnsafe ()
{
delete[] buffer;
}
private:
char* buffer;
};
class DerivedUnsafe {
public:
~DerivedUnsafe ()
{
BaseUnsafe::~BaseUnsafe();
}
};
the explicic call will run fine, but then the implicit (automagic) call to the destructor will trigger double-delete and undefined behavior.
Looks like it is easy to avoid the UB in the second case. Just set buffer
to null pointer after delete[]
.
But will this help? I mean the destructor is expected to only be run once on a fully constructed object, so the optimizer could decide that setting buffer
to null pointer makes no sense and eliminate that code exposing the program to double-delete.
Is the compiler allowed to do that?
Standard 12.4/14
Once a destructor is invoked for an object, the object no longer exists; the behavior is undefined if the destructor is invoked for an object whose lifetime has ended (3.8).
So I guess the compiler should be free to optimize away the setting of buffer to null since the object no longer exists after calling the destructor.
But even if the setting of the buffer to null wasn't removed by the compiler, it seems like calling the destructor twice would result in UB.
Calling the destructor converts an object into raw memory. You cannot destruct raw memory; this is undefined behaviour. The C++ compiler is entitled to do anything it wants. While it is unlikely that it will turn your computer in cottage cheese, it might deliberately trigger a slap-on-the-wrist SEGFAULT (at least in debug mode).
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