C++

The Assignment Operators

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.

Back to Index