Introduction to Programming and C++

Contents Previous Topic Next Topic

By default, when the memory for an object is being reclaimed, either because a variable goes out of scope or because an explicit call to delete on a pointer was made, the memory for the member variables is collected.

There may, however, be other operations which must be performed before the memory for an object is reclaimed. This may be done inside a destructor.

The destructor for a class ClassName has the signature ~ClassName();. It cannot take any parameters and it has no return value.

We will give two examples:

  • The first example tracks the number of instances (however created) of the class, and
  • The second class has only one member variable, an int pointer, however, in the constructor, we allocate memory for an array of int's.

Example 1: Tracking

This class Counter tracks the number of instances, so each time a constructor is called, the static variable count is incremented, however, each time the destructor is called, the static variable count must be decremented.

Aside: note that static class functions are called using the class name, in this case, Counter::getCount().

#include <iostream>
using namespace std;

class Counter {
	private:
		static int count;

	public:
		Counter();
		Counter( const Counter & );
		~Counter();

		static int getCount();
};

int Counter::count = 0;

Counter::Counter() {
	++count;
}

Counter::Counter( const Counter & ) {
	++count;
}

Counter::~Counter() {
	--count;
}

int Counter::getCount() {
	return count;
}

int main() {
	cout << "0. There are " << Counter::getCount() << " counters" << endl;

	Counter a;
	Counter b;

	cout << "1. There are " << Counter::getCount() << " counters" << endl;

	{
		Counter c;
		cout << "2. There are " << Counter::getCount() << " counters" << endl;
	}

	cout << "3. There are " << Counter::getCount() << " counters" << endl;

	Counter * ptr;    // declaring a pointer does not create a new object

	cout << "4. There are " << Counter::getCount() << " counters" << endl;

	ptr = new Counter;

	cout << "5. There are " << Counter::getCount() << " counters" << endl;

	delete ptr;

	cout << "6. There are " << Counter::getCount() << " counters" << endl;

	return 0;
}

Because the variables a and b only go out of scope after the function int main() returns, we will never return to zero counters.

Example 2: A Class with an Array

#include <iostream>
using namespace std;

// declare the class and the << operator function
class Array;
ostream & operator << ( ostream &, const Array & );

class Array {
	private:
		int * array;
		const static int N = 10;

	public:
		Array( int );
		~Array();

	friend ostream & operator << ( ostream &, const Array & );
};

Array::Array( int n) {
	array = new int[N];

	for ( int i = 0; i < N; ++i ) {
		array[i] = i*n;
	}
}

Array::~Array() {
	cout << "deallocating memory from array with multiplier..." << array[1] << endl;
	delete [] array;
}

// print the entries of the array, e.g., 0-2-4-6-8-10-12-14-16-18

ostream & operator << ( ostream & out, const Array & rhs ) {
	out << rhs.array[0];

	for ( int i = 1; i < Array::N; ++i ) {
		out << " - " << rhs.array[i];
	}

	return out;
}

int main() {
	Array a( 5 );

	cout << "The variable a is " << a << endl;

	Array * ptr;

	ptr = new Array( 7 );

	cout << "The pointer is pointing to " << *ptr << endl;

	cout << "Calling delete on the pointer" << endl;

	delete ptr;

	cout << "Returning from int main()" << endl;

	return 0;
}

In each case, the destructor is called when the memory for the object is collected, either through an explicit call to delete [] or because the variable went out of scope.

Using delete versus delete[]

Consider the following program where each box stores 1 KiB of memory for an array of integers. Each time the constructor and destructor are called, a message is printed indicating how much memory is being allocated or deallocated. When a single box is allocated (new Box()), the constructor is called once, while when an array of boxes is allocated (new Box[2]), the constructor is called twice, once for each entry of the array. Using delete[] calls the destructor twice, while using delete calls the destructor only once with undefined results.

#include <iostream>
using namespace std;

// a demonstration class which allocates an array of 256 integers

class Box {
	private:
		int * array;

	public:
		Box() {
			cout << "Allocating 1 KiB" << endl;
			array = new int[256];
		}

		~Box() {
			cout << "Deallocating 1 KiB" << endl;
			delete [] array;
		}
};

int main() {
	cout << "Allocating memory for a single box" << endl;
	Box *box = new Box();
	cout << "Deallocating memory for a single box using delete" << endl;
	delete box;
	cout << endl;

	cout << "Allocating memory for an array of two boxes" << endl;
	box = new Box[2];
	cout << "Deallocating memory for an array of 2 boxes using delete[] (good)" << endl;
	delete [] box;
	cout << endl;

	cout << "Allocating memory for an array of two boxes" << endl;
	box = new Box[2];
	cout << "Deallocating memory for an array of 2 boxes using delete (bad)" << endl;
	delete box;
	cout << endl;

	return 0;
}

When this code is run on ecelinux, the result is that it silently fails, resulting in a memory leak:

{ecelinux:1} g++ main.cpp 
{ecelinux:2} ./a.out 
Allocating memory for a single box
Allocating 1 KiB
Deallocating memory for a single box using delete
Deallocating 1 KiB

Allocating memory for an array of two boxes
Allocating 1 KiB
Allocating 1 KiB
Deallocating memory for an array of two boxes using delete [] (correct)
Deallocating 1 KiB
Deallocating 1 KiB

Allocating memory for an array of two boxes
Allocating 1 KiB
Allocating 1 KiB
Deallocating memory for an array of two boxes using delete (wrong!)
Deallocating 1 KiB
{ecelinux:3}

Notice that 1 KiB is still left allocated.

When this code is run on eceweb, the result is more traumatic:

[ece250@eceweb ~]$ g++ main.cpp 
[ece250@eceweb ~]$ ./a.out 
Allocating memory for a single box
Allocating 1 KiB
Deallocating memory for a single box using delete
Deallocating 1 KiB

Allocating memory for an array of two boxes
Allocating 1 KiB
Allocating 1 KiB
Deallocating memory for an array of two boxes using delete [] (correct)
Deallocating 1 KiB
Deallocating 1 KiB

Allocating memory for an array of two boxes
Allocating 1 KiB
Allocating 1 KiB
Deallocating memory for an array of two boxes using delete (wrong!)
Deallocating 1 KiB
*** glibc detected *** ./a.out: munmap_chunk(): invalid pointer: 0x0958e00c ***
======= Backtrace: =========
/lib/libc.so.6(__libc_free+0x179)[0x8ef070]
/usr/lib/libstdc++.so.6(_ZdlPv+0x21)[0x2ea801]
./a.out[0x8048b5e]
/lib/libc.so.6(__libc_start_main+0xdc)[0x89d4e4]
./a.out(__gxx_personality_v0+0x65)[0x8048721]
======= Memory map: ========
00238000-00317000 r-xp 00000000 03:01 1327722    /usr/lib/libstdc++.so.6.0.8
00317000-0031b000 r-xp 000de000 03:01 1327722    /usr/lib/libstdc++.so.6.0.8
0031b000-0031c000 rwxp 000e2000 03:01 1327722    /usr/lib/libstdc++.so.6.0.8
0031c000-00322000 rwxp 0031c000 00:00 0 
00759000-0075a000 r-xp 00759000 00:00 0          [vdso]
0086b000-00884000 r-xp 00000000 03:01 1489369    /lib/ld-2.4.so
00884000-00885000 r-xp 00018000 03:01 1489369    /lib/ld-2.4.so
00885000-00886000 rwxp 00019000 03:01 1489369    /lib/ld-2.4.so
00888000-009b5000 r-xp 00000000 03:01 1489370    /lib/libc-2.4.so
009b5000-009b7000 r-xp 0012d000 03:01 1489370    /lib/libc-2.4.so
009b7000-009b8000 rwxp 0012f000 03:01 1489370    /lib/libc-2.4.so
009b8000-009bb000 rwxp 009b8000 00:00 0 
009c3000-009e6000 r-xp 00000000 03:01 1489673    /lib/libm-2.4.so
009e6000-009e7000 r-xp 00022000 03:01 1489673    /lib/libm-2.4.so
009e7000-009e8000 rwxp 00023000 03:01 1489673    /lib/libm-2.4.so
00c5f000-00c6a000 r-xp 00000000 03:01 1489674    /lib/libgcc_s-4.1.1-20070108.so.1
00c6a000-00c6b000 rwxp 0000a000 03:01 1489674    /lib/libgcc_s-4.1.1-20070108.so.1
08048000-08049000 r-xp 00000000 16:00 6227594    /home/ece250/public_html/intro/2n/a.out
08049000-0804a000 rw-p 00001000 16:00 6227594    /home/ece250/public_html/intro/2n/a.out
0958e000-095af000 rw-p 0958e000 00:00 0 
b7fab000-b7fad000 rw-p b7fab000 00:00 0 
b7fbb000-b7fbd000 rw-p b7fbb000 00:00 0 
bfc77000-bfc8d000 rw-p bfc77000 00:00 0          [stack]
Abort
[ece250@eceweb ~]$ 

Contents Previous Topic Top Next Topic