The Assignment Operator
The assignment operator can be overloaded
like any other operator. In cases where dynamically allocated
memory is used, the assignment operator MUST be overloaded to
avoid potential problems.
What is it?
- The assignment operator is the function operator=
- It's called when we assign one existing object to another existing object.
Money m1( 10, 25 );
Money m2;
m2 = m1;
Why haven't we heard of this before?
The compiler provides a default assignment operator, which up until now has
been sufficient.
The default assignment operator simply copies each of the data members from the
existing object on the right hand side into the existing object on the left hand side.
This is not sufficient if one or more of the data members points to
dynamically allocated memory.
If this sounds strangely like the copy constructor, it is. But there
are important differences.
Let's look at some code using our SmartArray class.
SmartArray a1( 50 ); // 50 ints
SmartArray a2( 150 ); // 150 ints
a2 = a1; // typical assignment statement
Before the assignment, we had this situation in memory
The statement a2 = a1;
calls the assignment operator ( operator= ) for the SmartArray class.
In particular the call is a2.operator=( a1 );.
If we don't provide operator=, the compiler uses the default behavior.
Like the default copy constructor, the default assignment operator does a
member-by-member (shallow) assignment. We get this picture in memory which is
once again fraught with problems like the copy constructor.
In fact, this is worse than the copy constructor. What happened to the
memory that was in a2 before the assignment? We've caused a memory leak.
The picture we'd like to have, without causing a memory leak, is this
Here's our first attempt at writing the SmartArray assignment operator
void SmartArray::operator= (const SmartArray& rhs)
{
// free the memory for the left-hand object
delete [ ] m_data;
// now make a deep copy of the right-hand object
m_size = rhs.m_size;
m_data = new int [ m_size ];
for (int j = 0; j < m_size; j++ )
m_data[ j ] = rhs.m_data [ j ];
}
But we're not done....
Recall that it's desirable for our classes to emulate the built-in data types.
In particular, we can do the following with built-in data types...
int bob, mary, sally;
bob = mary = sally; // statement 1
bob = bob; // statement 2
(bob = mary) = sally; // statement 3
...so our objects should also support these statements, even if they
may make no sense.
- Statement 1 is a common thing to do. It's called cascading assignment.
To accomplish this, operator= must return a reference to a SmartArray.
- Statement 2 is meaningless, but allowable by the language.
To support this without causing a problem, operator= must check for this case
(this is called self-assignment).
- Statement 3 is odd, but valid. To support this, operator= must return a
reference to a non-const SmartArray object. (Debatable -- your text does not do this.)
So, our correct version of operator= is
SmartArray& // return a reference to non-const SmartArray
SmartArray::operator= (const SmartArray& rhs)
{
if (this != &rhs) // do nothing if the code is bob = bob;
{
// free the memory for the current array
delete [ ] m_data;
// make a copy of the rhs
m_size = rhs.m_size;
m_data = new int [m _size ];
for (int j = 0; j < m_size; j++ )
m_data[ j ] = rhs.m_data [ j ];
}
return *this; // for cascading assignment
}
What's "this"
The compiler provides each object with a special variable named this.
this is a pointer to the object which invoked the member function.
So whenever we write m_size, we could also have written
this->m_size;, but there's no reason to do so and it's just more code.
For the assignment operator, however, this becomes important.
We check for self-assignment with the condition this != &rhs which checks
if the address of the object that invoked the assignment operator (the left-hand
operand) is different from the address of the right-hand operand.
When operator= returns, it has to return a reference to an object?
You might ask, "Which object"? The answer is "the object on the left-hand-side
of the assignment" which is the one that invoked the function. How do
we reference that object? Using "this". If "this" is a pointer to that object,
then "*this" dereferences the pointer and is the object itself.
Last Modified: Monday, 28-Aug-2006 10:16:05 EDT