While working on lebookread I realized that 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;
}
base.h
#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.cpp