C++ destructors
Basics
A constructor function is called when an object's created, and a destructor function when the object's destroyed. Sometimes the default functions suffice, but you sometimes need to write your own. Here's a simple class with a constructor and destructor that print output to show when they're called.
class Base { public: string name; Base(string n) {name=n; cout << "Base Constructor for " << name << endl;} ~Base() {cout << "Base Destructor for " <<name << endl;} };
To see this in operation, try this -
#include <string> #include <iostream> using namespace std; class Base { public: string name; Base(string n) {name=n; cout << "Base Constructor for " <<name << endl;} ~Base() {cout << "Base Destructor for " << name << endl;} }; int main() { Base b("base 1"); }
Note that when the variable b
goes out of scope, its destructor is called. If the constructor uses new
to claim a lot of memory, it's important that the destructor's eventually called to free the memory, otherwise the process will use up resources. At the moment everything's ok - the variable b
is created and destroyed.
New and Delete
Let's add a line to main
Base *bp=new Base("base 2");
If the code's compiled and run now, you'll see that the new variable is created, but it's not destroyed when bp
dies. The memory used by the pointer bp
is freed, but not what bp
is pointing to. To solve that add
delete bp;
to the end of the program. Recompile and run to confirm that constructors and destructors are being called appropriately.
Inheritance
Now let's use inheritance. The following class uses Base
-
class Derived: public Base { public: Derived(string n):Base(n) { cout << "Derived Constructor for " <<name << endl;} ~Derived() {cout << "Derived Destructor for " <<name << endl;} };
Add this class definition to your code and try it out using the following main
function
int main() { Derived d("derived1"); Base *bp=new Derived("derived2"); delete bp; }
This time we've remembered to delete bp
. Note that when an object of type Derived
is created, the Base constructor then the Derived constructor is called. A similar thing should happen when the object's destroyed - the Derived
destructor then the Base
destructor is called. For the "derived1" object, that's the case, but there's a problem with the "derived2" variable. Even though bp
points to an object of type Derived
, only the destructor of the Base is called. This may lead to resources not being freed.
Virtual Functions
To fix this, change the Base destructor to
virtual ~Base() {cout << "Base Destructor for " << name << endl;}
Rather than just trusting the type of bp
to decide what delete should do (it's pointing to a Base which is why the Base constructor is called), the virtual
keyword causes the code to determine what bp
is really pointing to (in this case, a Derived object) and calls that object's destructor instead.
It's for this reason that there's a rule of thumb saying "make base classes' destructors virtual when they're meant to be manipulated polymorphically"
Exceptions and destructors
There's another rule of thumb saying that you should never throw exceptions from a destructor. If you don't know what exceptions are, I suggest you stop reading now.
The following code is adapted from Intel's documentation. It creates a bad class that throws from its destructor, and redefines the new and delete operators for it so that they print something when they're called.
#include <vector> #include <algorithm> #include <iostream> #include <iterator> using namespace std; #include <cstdlib> class Bomb { // bad class throws exception from destructor string name; public: Bomb(string s) : name(s) {cout <<"constructor called for " << name << endl;} ~Bomb() {cout <<"destructor called for " << name << endl; throw "boom"; } // new is by default a static member. The "throw()" promises not to // throw exceptions from this method void * operator new(size_t n) throw() { cout <<"operator new called\n"; return malloc(n); } //by default a static member void operator delete(void *p) throw() { cout << "operator delete called\n"; // never gets here if (p != 0) free(p); } }; void f() { Bomb Bomb1("Bomb1"); Bomb *pBomb = new Bomb("Bomb2"); try { delete pBomb; } catch (...) { // Gets here but leaks storage. Print output shows that // operator new was called but operator delete was not. cout <<"caught exception from destructor\n"; } // program dies here: can't throw two exceptions // at the same time, so terminates throw "no boom"; // program dies here } int main() { try { f(); } catch (char *message) { cout << "f threw " << message << endl; // never gets here } }
When you run this you'll get something like
constructor called for Bomb1 operator new called constructor called for Bomb2 destructor called for Bomb2 caught exception from destructor terminate called after throwing an instance of 'char const*' Aborted
Bomb1
is created using the constructor. Then Bomb2
is created and straight away deleted. The delete operation is within a try ... catch
block so the exception thrown by the destructor is caught. The problem is that when an object is deleted, two things happen: first the destructor is called and second the delete operator is called. If the destructor throws an exception, delete won't be called and resources will be wasted.
There's another problem. At the end of the f
function the Bomb2
exception is still active. The lifetime of Bomb1
is over so its destructor is called, causing an exception. Consequently, there's a queue of exceptions to be dealt with. C++ can't deal with that so the program terminates.