Virtual Destructors

When a derived object is instantiated and upcast to its base class data type, a problem arises when the derived object goes out of scope or is removed by way of the keyword delete, inasmuch as the compiler thinks that since it has been upcast it therefore uses the base destructor method, which can potentially leave copies of the derived objects untouched (consuming memory or leading to memory leaks).

 

Therefore the general rule is to always make destructors virtual when dealing with derived classes.

 

In this example a base class object is created, a base class data type pointer is assigned to the object and its method is invoked.  The base class data type pointer is then assigned to a new instance of the derived (Cat) class and its overridden method is invoked.  The (base class data type) pointer is then deleted. The result is that (behind the scenes, the derived object is still consuming memory): 

#include <iostream>
#include <string>
using namespace std;

class Animal {
	public:
		virtual void speak() {
			cout << "Base: I'm Alive." << endl ;
		}
//		virtual ~Animal(){cout << "Base: I'm dead."<<endl;}
};

class Cat : public Animal {
	public :
		void speak(){
			cout << "Derived: Meow." << endl ;
	}
//		virtual ~Cat(){cout << "Cat destroyer"<<endl;}
};

int main() {

	Animal feline ;
	Animal *ptr = &feline;
	ptr->speak() ;

	ptr = new Cat ;
	ptr->speak() ;

	delete ptr ;

	return 0;
}

Compile & Run:

Base: I'm Alive.
Derived: Meow.

 

 

Uncommenting line 10 provides a virtual destructor for the base class:

Compile & Run:

Base: I'm Alive.
Derived: Meow.
Base: I'm dead.
Base: I'm dead.

 

OK a little better, but the derived object still only has the base destructor acting upon it thereby leaving the derived portion potentially still consuming memory.

 

Now uncommenting line 18, we finally have the desired action whereby the derived object is fully destroyed:

Compile & Run:

Base: I'm Alive.
Derived: Meow.
Cat destroyer
Base: I'm dead.
Base: I'm dead.

 

Leave a Reply