* C++ Derived Classes and Object Destruction
Posted on May 29th, 2010 by John. Filed under programming.
While working on lebookread I realized that the the destructor for my reader classes would never be called. lebook read has a base class (FormatReader) that exports all of the necessary functionality for use by applications using the library. All of the readers are a subclass of FormatReader. The library will find the appropriate reader for the specified ebook create a reader object and return it as a FormatReader pointer.
When you are dealing with a pointer p of type base that points to an object of type derived you need to take special care. To have the destruction of p call the derived and base destructor the base class must have a virtual destructor. Otherwise only the base class’s destructor will be called. This is one of those little things to look out for when dealing with C++.
Here is some example code to demonstrate the above.
main.cpp
#include <iostream> #include "base.h" #include "deriv.h" using namespace std; int main(int argc, char** argv) { cout << "Base *b = new Base();" << endl; cout << "delete b;" << endl; Base *b = new Base(); cout << " --- " << endl; delete b; cout << " --- " << endl; cout << "b destroyed" << endl; cout << endl; cout << "Deriv *d = new Deriv();" << endl; cout << "delete d" << endl; Deriv *d = new Deriv(); cout << " --- " << endl; delete d; cout << " --- " << endl; cout << "d destroyed" << endl; cout << endl; cout << "Base *c = new Deriv();" << endl; cout << "delete c" << endl; Base *c = new Deriv(); cout << " --- " << endl; delete c; cout << " --- " << endl; cout << "c destroyed" << endl; return 0; }
#ifndef BASE_H #define BASE_H class Base { public: ~Base(); }; #endif /* BASE_H */
base.cpp
#include <iostream> #include "base.h" using namespace std; Base::~Base() { cout << "base dest" << endl; }
deriv.h
#ifndef DERIV_H #define DERIV_H #include "base.h" class Deriv : public Base { public: ~Deriv(); }; #endif /* DERIV_H */
deriv.cpp
#include <iostream> #include "deriv.h" using namespace std; Deriv::~Deriv() { cout << "deriv dest" << endl; }
The output of this will be:
$ ./a.out Base *b = new Base(); delete b; --- base dest --- b destroyed Deriv *d = new Deriv(); delete d --- deriv dest base dest --- d destroyed Base *c = new Deriv(); delete c --- base dest --- c destroyed
Notice that when destroying c only the Base’s destructor is called. To fix this and have both Base and Deriv’s destructors called just make Base’s destructor virtual.
#ifndef BASE_H #define BASE_H class Base { public: virtual ~Base(); }; #endif /* BASE_H */
This simple change will cause the output to become:
$ ./a.out Base *b = new Base(); delete b; --- base dest --- b destroyed Deriv *d = new Deriv(); delete d --- deriv dest base dest --- d destroyed Base *c = new Deriv(); delete c --- deriv dest base dest --- c destroyed
Now when destroying c the destructor for both Base and Deriv are called.
To compile
$ g++ base.cpp deriv.cpp main.cppOne Response to “C++ Derived Classes and Object Destruction”
Tags
Archives
- February 2012 (1)
- January 2012 (3)
- December 2011 (2)
- November 2011 (1)
- October 2011 (3)
- September 2011 (9)
- August 2011 (15)
- July 2011 (5)
- June 2011 (3)
- May 2011 (4)
- April 2011 (2)
- March 2011 (2)
- February 2011 (4)
- January 2011 (4)
- December 2010 (2)
- November 2010 (1)
- October 2010 (1)
- August 2010 (3)
- July 2010 (4)
- June 2010 (1)
- May 2010 (2)
- March 2010 (1)
- January 2010 (8)
- December 2009 (5)
- November 2009 (6)
- October 2009 (4)
- September 2009 (2)
- August 2009 (6)
- July 2009 (6)
- June 2009 (4)
- May 2009 (6)
- April 2009 (4)
- March 2009 (2)
- February 2009 (4)
- January 2009 (4)
- December 2008 (7)
- November 2008 (2)
May 30th, 2010 at 10:17 am
Right. This is because the keyword virtual causes function lookup to occur at runtime – by selecting the function from the virtual function table. If you omit the keyword, then function lookup is done at compile time, and the address of the function corresponding to the type of variable is chosen, which means in your middle case above, the base function is chosen, since the variable is a base variable. Once you make it virtual, it doesn’t bind at compile time, it consults the lookup table at runtime.