class Base // Base Class
{
	public:
		~Base () {} // Destructor for Base()
};
 
class Derived : public Base // Derived Class
{
	public:
		~Derived () {} // Destructor for Derived()
};
 
Base *obj = new Derived(); // Create Base pointer that points to Derived
 
// Things go wrong here!
delete obj; 

When obj is being deleted, the destructor for Base will be invoked. This is dangerous because it leaks memory → Derived’s destructor is not called, hence the contents of the Derived class will remain dangling Virtual destructors make sure that the right destructors are invoked in an intuitive manner

class Base // Base Class
{
	public:
		virtual ~Base () {} // Destructor for Base()
};
 
class Derived : public Base // Derived Class
{
	public:
		~Derived () {} // Destructor for Derived()
};
 
Base *obj = new Derived(); // Create Base pointer that points to Derived
 
// All good!
delete obj; 

Here, when obj is being deleted, the destructor of Derived will first be called and then Base’s destructor is called, thus ensuring no memory leaks and a safe cleanup.