Take a look at this C++ class:
class CMyClass
{
private:
int *m_pPtr;
public:
CMyClass()
: m_pPtr(NULL)
{
}
void Set(int *pPtr)
{
// Clean up
if (m_pPtr != NULL)
{
delete m_pPtr;
m_pPtr = NULL;
}
// Assign
if (pPtr != NULL)
{
// Make a copy
m_pPtr = new int;
*m_pPtr = *pPtr;
}
}
int *Get() const
{
return m_pPtr;
}
~CMyClass()
{
// Clean up
if (m_pPtr != NULL)
{
delete m_pPtr;
m_pPtr = NULL;
}
}
};
You'll notice that there is a pointer member variable.
If you use the class as follows, you won't have any problems:
void MyFunction()
{
CMyClass var;
int i = 5;
var.Set(&i);
int *pPtr = var.Get();
}
However, you will have problems if you do this:
void MyFunction(CMyClass var2)
{
int *pPtr = var2.Get();
}
void MyOtherFuncion()
{
CMyClass var;
int i = 5;
var.Set(&i);
MyFunction(var);
int *pPtr = var.Get();
}
Why?
Because when MyFunction() is called, the variable var is copied to var2. When this happens, if you inspect the pointer of var and var2, you'll notice that the m_pPtr member variables not only contain the same value (5, as is correct to think), but they also point to the exact same address in memory (not expected).
In the above example, because the m_pPtr member variables point to the same address in memory, that memory will be deleted twice: once by var2 when exiting MyFunction(), but also by var when exiting MyOtherFunction(). This should result in a crash.
The solution? Copy constructors.
If you add the following function to the above class, your problem is solved:
class CMyClass
{
// Other functions
CMyClass(const CMyClass &other)
: m_pPtr(NULL)
{
Set(other.m_pPtr);
}
};
If you run the previous problematic code with the new copy constructor, the program won't have the same issues anymore. The m_pPtr member variables will continue to contain the same value (5), but they will point to different addresses in memory. This will allow each to delete it's own copy in it's destructor.
Why pass other by reference (by using &) ?
If other was not passed by reference, then the copy constructor would be called when calling the copy constructor and this would lead to a (potential) infinite loop.
Why is this different than operator = ?
While the operator = and the copy constructor may perform very similar functionality, they are each called in different contexts.
CMyClass CreateVar()
{
CMyClass var;
int i = 10;
var.Set(&i);
return var; // Copy constructor
}
void MyFunction()
{
CMyClass var;
int i = 5;
var.Set(&i);
CMyClass var2(var); // Copy Constructor
CMyClass var3 = var; // operator =
CMyClass var4;
var4 = var; // operator =
CMyClass var5 = CreateVar(); // operator =
}
This is assuming that the compiler doesn't change things by optimizing (especially in the call to CreateVar()).
I hope this helps.
Thus endeth the lesson.