Object-Oriented Design Choices by Dingle Adair;

Object-Oriented Design Choices by Dingle Adair;

Author:Dingle, Adair;
Language: eng
Format: epub
Publisher: CRC Press LLC
Published: 2020-12-10T00:00:00+00:00


6.1.2When Inheritance Leaks Memory: C++ Destructors

C++ class designers must carefully manage heap memory as shown in Chapters 2 and 3. To avoid heap memory leaks and data corruption, the class designer must explicitly determine copy semantics: suppress copying, or efficiently support deep copying. A destructor must be defined for deallocation. The client is not responsible for invoking destructors; the compiler patches in destructor calls when stack objects go out of scope or when the delete operator is invoked for the release of heap objects.

C++ memory management becomes more complex when derived classes allocate heap memory. Destructors fire in reverse order of constructors. However, when delete is invoked through a polymorphic handle, binding affects outcome. To release a stack allocated Base class object, the compiler invokes only the Base destructor. To release a stack allocated Derived class object, the Derived destructor is invoked first, followed by the Base destructor. The Base destructor is implicitly invoked from the Derived destructor (a call to the Base destructor is the last instruction in the Derived destructor).

If a Base class pointer, myPtr, contains the address of a Base object, delete myPtr invokes the Base class destructor: no problem. What if myPtr holds the address of a Derived object? The compiler resolves calls based on the type of the pointer. With (default) static resolution of the destructor, only the Base class destructor is invoked so the Derived class destructor never runs. Failure to invoke the Derived class destructor leaks memory when the Derived class allocates heap memory. Ouch!

Example 6.2 illustrates such a hidden memory leak. Test this code yourself. When delete is called with Base class pointer b, the destructor call is statically bound so only ∼Base() fires. Through no fault of the client, the 500 integers allocated on the heap for a Derived object leak because ∼Derived() does not run. In statement #3, the client correctly calls delete for each heap object but with statically resolved destructors, only ∼Base() is invoked. The client has followed convention (match every new with delete) and yet there is an invisible memory leak.

With heterogeneous collections, Base class pointers often hold addresses of Derived class objects. To ensure that the appropriate destructor is called through a base class pointer, destructor invocation must be postponed. The (sub)type of the object whose address in held in the base class pointer must be examined at run-time in order to execute the proper destructor(s). The setup is the same as with other virtual methods. A Base class pointer may hold the address of Derived object so if the destructor is dynamically bound, the subtype resolved at run-time determines which destructor is called first (remember invocation of ∼Derived() automatically yields ∼Base()).

The fix is easy: make the Base destructor virtual. That’s all. No need to replicate Example 6.2. All code is the same except the keyword virtual is placed in front of the destructor in statement #1. Now the address of each class destructor will be placed in its vtab (because once virtual, always virtual!). Hence,



Download



Copyright Disclaimer:
This site does not store any files on its server. We only index and link to content provided by other sites. Please contact the content providers to delete copyright contents if any and email us, we'll remove relevant links or contents immediately.