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.
clear()
method (private, if it doesn't make sense for it to be part of the public interface) and call it from both places. Even if it's not technically necessary in a particular situation, it will look a lot less weird and is substantially less likely to trip up the next person who has to maintain your code. – Sic