How do virtual destructors work?

2019-04-08 05:50发布

问题:

Few hours back I was fiddling with a Memory Leak issue and it turned out that I really got some basic stuff about virtual destructors wrong! Let me put explain my class design.

class Base
{
  virtual push_elements()
  {}
};

class Derived:public Base
{
vector<int> x;
public:
   void push_elements(){ 
      for(int i=0;i <5;i++)
         x.push_back(i); 
   }
};

void main()
{
    Base* b = new Derived();
    b->push_elements();
    delete b;
}

The bounds checker tool reported a memory leak in the derived class vector. And I figured out that the destructor is not virtual and the derived class destructor is not called. And it surprisingly got fixed when I made the destructor virtual. Isn't the vector deallocated automatically even if the derived class destructor is not called? Is that a quirk in BoundsChecker tool or is my understanding of virtual destructor wrong?

回答1:

Deleting a derived-class object through a base-class pointer when the base class does not have a virtual destructor leads to undefined behavior.

What you've observed (that the derived-class portion of the object never gets destroyed and therefore its members never get deallocated) is probably the most common of many possible behaviors, and a good example of why it's important to make sure your destructors are virtual when you use polymorphism this way.



回答2:

If the base class does not have a virtual destructor, then the result of your code is undefined behaviour, not necessarily the wrong destructor being called. This is presumably what BoundsChecker is diagnosing.



回答3:

Although this is technically undefined, you still need to know the most common method of failure in order to diagnose it. That common method of failure is to call the wrong destructor. I don't know of any implementation that will fail in any other manner, though admittedly I only use two implementations.

The reason this happens is the same reason the 'wrong' function will get called when you try to override a non-virtual member function and call it through a base pointer.



回答4:

If the destructor is not virtual then the Base destructor will be called. The base destructor cleans up the Base object and finishes. There is no way for the base object destructor to know about the derived object, it must be the derived destructor called, and the way to do that, as with any function, is to make the destructor virtual.



回答5:

From the C++ FAQ Lite: "When should my destructor be virtual?" Read it here. (C++ FAQ Lite is an excellent source for all your questions related to C++, by the way).



回答6:

In C++, a trivial destructor is a recursively defined concept -- it's a destructor that the compiler wrote for you when every member of the class (and every base class) has a trivial destructor. (There's a similar concept called the trivial constructor.)

When an object with a nontrivial destructor is included in an object (like the vector in your example), then the destructor of the outside object (like your Derived) in is no longer trivial. Even though you didn't write destructor, the C++ compiler automatically wrote a destructor that calls the destructors of any members that have destructors.

So, even though you didn't write anything, the caveats of writing a non-virtual destructor still apply.



回答7:

if you are coming from c#, then you were right in wondering why vector is not automatically de-allocated. But in c++, automatic memory management is not availble unless you use the Microsoft Manged Extesions to C++ (C++/CLI).

since there is no destructor in Base class that is virtual, the derived class object will never be freed and there-by you leak the memory allocated for the vector data member of the the derived class.



回答8:

Destructor is the member function of the class whose name is the same name of the class name and it is preceded by the tilde sign(~). Destructor is used to destroy the object of the class when object goes out of scope or you can say that all clean up of class destruction are to be done in destructor. All the memory gets allocated during construction of the object in class gets destructed (or memory release) when object goes out of scope.

Find more details with example on BoundsCheck