In this example we have a class that contains a pointer. When an instance of the class is created, the pointer has memory allocated, so the destructor must delete that memory.
class Wrong
{
public:
Wrong(int size =0){if (size)_mem = new int [size]; else_mem= NULL; _size = size;}
~Wrong(){ if ( _mem)delete [] _mem;}// the destructor
int& operator [] (int i){return _mem[i];}
private:
int *_mem;
int _size; // how large is mem
}
In the following code, the function foo passes wrong as a value parameter, and upon return
from the function, mem points to memory that has been deleted.
void foo(Wrong w)
{
cout << w[1] << endl;
}
void main()
{
Wrong wrong(5);
wrong[0] = 1;
wrong[1] = 2;
wrong[2] = 3;
foo(wrong);
cout << wrong[1] << endl;
}
When the program tries to execute the output, there is a major error since wrong[1] now
points to no allocated location. In the windows environment there error is not obvious
until you trace the constructor calls. Then you will see the same memory released twice.
This means that you can't be sure what will happen the second time you access wrong.
You can run the complete version available here.
This error can be corrected by adding the copy constructor to the definition of Wrong.
The copy constructor is called by c++ whenever a variable is passed by value to a function.
The compiler provides a simple copy constructor which simply sets the copy's version of
mem to the same pointer as the original variable's mem. When the destructor is called
for the parameter as the program leaves the function call, the location deleted is that
of the mem in the variable. To make sure the two versions have different pointers, you
need to create the copy constructor. The compy constructor takes the form
X(const X &)
in this case:
Wrong ( const Wrong &w)
This copy constructor first allocates new memory, then copies the data from the old variable:
Wrong::Wrong( const Wrong &w)
{
_mem = new int [w._size];
_size = w._size;
for ( int i = 0; i < _size; i++)
_mem[i] = w._mem[i];
}
Instead of using the primitive c array, we should have used a c++ arrary such as that defined in darray.h. This type of container class has a built-in memory management, so there is no need to create either the destructor or copy constructor in the class Right defined here.
class Right
{
public:
Right(int size=0) :_mem(n){}
private:
Array _mem;
};
If wrong is replaced by right in our example, then the schematic below shows how the
copy constructor for array alters the access count.
void main()
{
| right[0] = 1; right[1] = 2; right[2] = 3; | |
| foo(right); | |
| cout << right[1] << endl; |