Non-virtual trivial destructor + Inheritance

2019-05-24 07:59发布

问题:

Given that a class and all its subclasses need no more than the default destructor to release their resources if stored in a variable of the exact type (or pointer to the exact type), can a subclass leak memory if referenced by a base class pointer and then deleted by that pointer?

Example:

#include <memory>

class A {
};

class B : public A {
public:
    B () : pInt(new int) {}
    auto_ptr<int> pInt; // this is what might leak... possibly more will though
};

void will_this_leak () {
    A *pA = new B();
    delete pA;
}

回答1:

"Leak memory"? Why are you talking about leaking memory specifically?

The code you posted produces undefined behavior. Anything can happen in this case: memory leaked, hard drive formatted, program crashed, etc.

P.S. I know that there's a popular urban legend out there that performing polymorphic destruction without a virtual destructor "might leak memory". I don't know who invented that nonsense and why they decided to use "leaking memory" as the primary scenario for what might happen. In reality the behavior in this case has absolutely nothing to do with "leaking memory". The behavior is simply undefined.

From the practical point of view, in your particular case it is rather obvious that there's no real chance for the destructor of your auto_ptr to get called, so the memory owned by that auto_ptr will certainly be leaked. But again, this is the least of this code's problems.



回答2:

It doesn't matter whether they can leak or not. The C++ standard says that deleting a derived class via a base pointer is undefined behavior if the base does not have a virtual destructor. Specifically from 5.3.5/3:

In the first alternative (delete object), if the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined.

So once you have done such a deletion, your program is in an undefined state, and all questions about leaks are moot.



回答3:

Yes, this will leak. When you delete the A*, it calls ~A(). Since ~A() is not virtual, it won't know that ~B() needs calling too.

Assuming, of course, that B inherits from A. I'm guessing that's a typo in your question -- the code as it stands won't compile :)



回答4:

The code as of now exhibits undefined-behavior.

If the static type of the operand is different from the dynamic type, then the static type becomes the base class and it's destructor must be virtual. Else the behavior is undefined.

#include <memory>

class A {

    public :
    virtual ~A() {} // This makes the derived sub-object destruction first

};

class B : public A {
public:
    B () : pInt(new int) {}
    auto_ptr<int> pInt; 

    /* There is no need to write any custom destructor
       in this case. auto_ptr will effectively handle deallocating
       the acquired resources from free store.
    */
};

void will_this_leak () {
    A *pA = new B();
    delete pA;
}

With the above changes made, there shouldn't be any undefined behavior or memory leaks.