c:c_virtual_destructors_-_how_to_avoid_memory_leaks
C - C++ Virtual Destructors - How to Avoid Memory Leaks
Inheritance is a very commonly used paradigm in C++.
- Inheritance also lends itself to virtual methods, where implementation is provided by any specific subclasses.
- However, once an inheritance hierarchy is created, with memory allocations occurring at each stage in the hierarchy, it is necessary to be very careful about how objects are destroyed so that any memory leaks are avoided.
- In order to achieve this, we make use of a virtual destructor.
In simple terms, a virtual destructor ensures that when derived subclasses go out of scope or are deleted the order of destruction of each class in a hierarchy is carried out correctly.
- If the destruction order of the class objects is incorrect, in can lead to what is known as a memory leak.
- This is when memory is allocated by the C++ program but is never deallocated upon program termination.
- This is undesirable behaviour as the operating system has no mechanism to regain the lost memory (because it does not have any references to its location!).
- Since memory is a finite resource, if this leak persists over continued program usage, eventually there will be no available RAM (random access memory) to carry out other programs.
For instance, consider a pointer to a base class (such as PayOff) being assigned to a derived class object address via a reference.
- If the object that the pointer is pointing to is deleted, and the destructor is not set to virtual, then the base class destructor will be called instead of the derived class destructor.
- This can lead to a memory leak.
Consider the following code:
class Base { public: Base(); ~Base(); }; class Derived : public Base { private: double val; public: Derived(const double& _val); ~Derived(); } void do_something() { Base* p = new Derived; // Derived destructor not called!! delete p; }
NOTE: What is happening here?
- Firstly, we create a base class called Base and a subclass called Derived.
- The destructors are NOT set to virtual.
- In our do_something() function, a pointer p to a Base class is created and a reference to a new Derived class is assigned to it.
- This is legal as Derived is a Base.
However, when we delete p the compiler only knows to call Base's destructor as the pointer is pointing to a Base class.
- The destructor associated with Derived is not called and val is not deallocated.
A memory leak occurs!
Now consider the amended code below.
The virtual keyword has been added to the destructors:
class Base { public: Base(); virtual ~Base(); }; class Derived : public Base { private: double val; public: Derived(const double& _val); virtual ~Derived(); } void do_something() { Base* p = new Derived; // Derived destructor is called delete p; }
NOTE: What happens now?
- Once do_something() is called, delete is invoked on the pointer p.
- At code execution-time, the correct destructor is looked up in an object known as a vtable.
- Hence the destructor associated with Derived will be called prior to a further call to the destructor associated with Base.
- This is the behaviour we originally desired.
- val will be correctly deallocated.
No memory leak this time!
c/c_virtual_destructors_-_how_to_avoid_memory_leaks.txt · Last modified: 2023/06/19 21:20 by peter