Writing a Safer Deconstructor: Virtual Destructor in Polymorphism

When you use polymorphic objects, the base class must have virtual deconstructor, or it may cause memory leak.

Look the example:

#include <iostream>

using std::cout;
using std::endl;

void Debug(const char* str)
{
  cout << str << endl;
}

class Animal
{
public:
  Animal()
  {
    Debug("Animal()");
  };

  ~Animal()
  {
    Debug("~Animal()");
  };
};

class Cat : public Animal
{
public:
  Cat()
  {
    Debug("Cat()");
  };

  ~Cat()
  {
    Debug("~Cat()");
  };
};

int main() {

  Debug("-- Round 1 start --");
  {
    Cat c;
  }
  Debug("-- Round 1 End --");

  Debug("-- Round 2 start --");
  {
    Animal *a = new Cat();
    delete a;
  }
  Debug("-- Round 2 End --");

  return 0;
}

The output is:

-- Round 1 start --
Animal()
Cat()
~Cat()
~Animal()
-- Round 1 End --
-- Round 2 start --
Animal()
Cat()
~Animal()
-- Round 2 End --

It may cause memory leak! The object hold by Cat will not be released if ~Cat isn't called. It will be clearer when we add a member object in Cat:

#include <iostream>

using std::cout;
using std::endl;

void Debug(const char* str)
{
  cout << str << endl;
}

class Member
{
public:
  Member()
  {
    Debug("Member()");
  }

  ~Member()
  {
    Debug("~Member()");
  }
};

class Animal
{
public:
  Animal()
  {
    Debug("Animal()");
  };

  ~Animal()
  {
    Debug("~Animal()");
  };
};

class Cat : public Animal
{
public:
  Cat()
  {
    Debug("Cat()");
  };

  ~Cat()
  {
    Debug("~Cat()");
  };
private:
  Member m;
};

int main() {

  Debug("-- Round 1 start --");
  {
    Cat c;
  }
  Debug("-- Round 1 End --");

  Debug("-- Round 2 start --");
  {
    Animal *a = new Cat();
    delete a;
  }
  Debug("-- Round 2 End --");

  return 0;
}

The result is:

-- Round 1 start --
Animal()
Member()
Cat()
~Cat()
~Member()
~Animal()
-- Round 1 End --
-- Round 2 start --
Animal()
Member()
Cat()
~Animal()
-- Round 2 End --

You can see that no ~Member() is called. Thus, the memory occupied by Member m is leaked!

Virtual Deconstructor

The solution is easy. Simply adding a virtual before ~Animal can solve the problem.

...
...
class Animal
{
public:
  Animal()
  {
    Debug("Animal()");
  };

  virtual ~Animal()
  {
    Debug("~Animal()");
  };
};
...
...

The output will be

-- Round 1 start --
Animal()
Member()
Cat()
~Cat()
~Member()
~Animal()
-- Round 1 End --
-- Round 2 start --
Animal()
Member()
Cat()
~Cat()
~Member()
~Animal()
-- Round 2 End --

The ~Cat() and ~Member() will both be called, so there is no leak problem anymore!

Conclusion

Never inherit a class that has no virtual deconstructor.

results matching ""

    No results matching ""