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.