Are there any unexpected consequences of calling a

2019-08-26 10:43发布

问题:

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?

回答1:

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:

  1. It will call its own base version (your assignment operator).
  2. Your assignment operator calls the destructor. This is a virtual call.
  3. The derived destructor destroys the members of the derived class.
  4. The base destructor destroys the members of the base class.
  5. 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.
  6. The copy constructor constructs the base's members.
  7. 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.



回答2:

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.