|

The
fourth and final function that is supplied by the
compiler, if you don’t specify one, is the assignment
operator (operator=()). This operator is called
whenever you assign to an object. For example:
CAT
catOne(5,7);
CAT
catTwo(3,4);
//
... other code here
catTwo
= catOne;
Here,
catOne is created and initialized with itsAge equal
to 5 and itsWeight equal to 7. catTwo is then created
and assigned the values 3 and 4.
After
a while, catTwo is assigned the values in catOne.
Two issues are raised here: What happens if itsAge
is a pointer, and what happens to the original values
in catTwo?
Handling
member variables that store their values on the
free store was discussed earlier during the examination
of the copy constructor. The same issues arise here,
as you saw illustrated in Figures 10.1 and 10.2.
C++
programmers differentiate between a shallow or member-wise
copy on the one hand, and a deep copy on the other.
A shallow copy just copies the members, and both
objects end up pointing to the same area on the
free store. A deep copy allocates the necessary
memory. This is illustrated in Figure 10.3.
There
is an added wrinkle with the assignment operator,
however. The object catTwo already exists and has
memory already allocated. That memory must be deleted
if there is to be no memory leak. But what happens
if you assign catTwo to itself?
catTwo
= catTwo;
No
one is likely to do this on purpose, but the program
must be able to handle it. More important, it is
possible for this to happen by accident when references
and dereferenced pointers hide the fact that the
assignment is to itself.
If
you did not handle this problem carefully, catTwo
would delete its memory allocation. Then, when it
was ready to copy in the memory from the right-hand
side of the assignment, it would have a very big
problem: The memory would be gone.
To
protect against this, your assignment operator must
check to see if the right-hand side of the assignment
operator is the object itself. It does this by examining
the this pointer. Listing 10.15 shows a class with
an assignment operator.
Listing
10.15. An assignment operator.
1:
// Listing 10.15
2:
// Copy constructors
3:
4:
#include <iostream.h>
5:
6:
class CAT
7:
{
8:
public:
9:
CAT(); // default constructor
10:
// copy constructor and destructor elided!
11:
int GetAge() const { return *itsAge; }
12:
int GetWeight() const { return *itsWeight; }
13:
void SetAge(int age) { *itsAge = age; }
14:
CAT operator=(const CAT &);
15:
16:
private:
17:
int *itsAge;
18:
int *itsWeight;
19:
};
20:
21:
CAT::CAT()
22:
{
23:
itsAge = new int;
24:
itsWeight = new int;
25:
*itsAge = 5;
26:
*itsWeight = 9;
27:
}
28:
29:
30:
CAT CAT::operator=(const CAT & rhs)
31:
{
32:
if (this == &rhs)
33:
return *this;
34:
delete itsAge;
35:
delete itsWeight;
36:
itsAge = new int;
37:
itsWeight = new int;
38:
*itsAge = rhs.GetAge();
39:
*itsWeight = rhs.GetWeight();
40:
return *this;
41:
}
42:
43:
44:
int main()
45:
{
46:
CAT frisky;
47:
cout << "frisky’s age: " << frisky.GetAge()
<< endl;
48:
cout << "Setting frisky to 6...\n";
49:
frisky.SetAge(6);
50:
CAT whiskers;
51:
cout << "whiskers’ age: " << whiskers.GetAge()
<< endl;
52:
cout << "copying frisky to whiskers...\n";
53:
whiskers = frisky;
54:
cout << "whiskers’ age: " << whiskers.GetAge()
<< endl;
55:
return 0;
56:
}
frisky’s age: 5
Setting frisky to 6...
whiskers’ age: 5
copying frisky to whiskers...
whiskers’ age: 6
OUTPUT: Listing 10.15 brings back the
CAT class, and leaves out the copy constructor and
destructor to save room. On line 14, the assignment
operator is declared, and on lines 30-41 it is defined.
ANALYSIS: On line 32, the current object
(the CAT being assigned to) is tested to see whether
it is the same as the CAT being assigned. This is
done by checking whether or not the address of rhs
is the same as the address stored in the this pointer.
This
works fine for single inheritance, but if you are
using multiple inheritance, as discussed in Unit
13, "Polymorphism," this test will fail. An alternative
test is to dereference the this pointer and see
if the two objects are the same:
if
(*this == rhs)
Of
course, the equality operator (==) can be overloaded
as well, allowing you to determine for yourself
what it means for your objects to be equal.
|