For example:
class Foo : public Bar
{
~Foo()
{
// Do some complicated stuff
}
Foo &operator=(const Foo &rhs)
{
if (&rhs != this)
{
~Foo(); // Is this safe?
// Do more stuff
}
}
}
Are there any unexpected consequences of calling the destructor explicitly with regard to inheritance and other such things?
Is there any reason to abstract out the destructor code into a void destruct()
function and call that instead?
Calling the destructor is a bad idea in the simplest case, and a horrible one the moment the code gets slightly more complex.
The simplest case would be this:
class Something {
public:
Something(const Something& o);
~Something();
Something& operator =(const Something& o) {
if (this != &o) {
// this looks like a simple way of implementing assignment
this->~Something();
new (this) Something(o); // invoke copy constructor
}
return *this;
}
};
This is a bad idea. If the copy constructor throws, you're left with raw memory - there isn't an object there. Only, outside the assignment operator, nobody notices.
If inheritance comes into play, things become worse. Let's say Something
is in fact a base class with a virtual destructor. The derived class's functions are all implemented with a default. In this case the derived class's assignment operator will do the following:
- It will call its own base version (your assignment operator).
- Your assignment operator calls the destructor. This is a virtual call.
- The derived destructor destroys the members of the derived class.
- The base destructor destroys the members of the base class.
- Your assignment operator calls the copy constructor. This isn't virtual; it actually constructs a base object. (If the base class isn't abstract. If it is, the code won't compile.) You have now replaced the derived object with a base object.
- The copy constructor constructs the base's members.
- The derived assignment operator does a memberwise assignment of the derived class's members. Which have been destroyed and not re-created.
At this point, you have multiple instances of UB heaped upon each other, in a glorious pile of complete chaos.
So yeah. Don't do that.
Absolutely not.
void f()
{
Foo foo, fee;
foo = fee; <-- a first foo.~Foo will be called here !
} <-- a second foo.~Foo will be called here and fee.~Foo as well !
As you can see you have 3 calls to destructor instead of expected 2 calls.
You should NOT use a *self-*destructor inside a constructor or a non-static method.