CSE 202: Computer Science II, Winter 2018
Assignment and operator overloading
Assignment

Even though copy initialization and copy assignment both use the simple assignment operator, they are different:

T object = other; // Copy initialization object = other; // Copy assignment

For non-class types, they both have the same effect: the value of other is copied and assigned to object, replacing whatever value object already had.

The potential presence of a copy constructor or copy assignment operator in T can alter what happens during copy initialization or copy assignment.

#include <iostream> struct copy_me { copy_me(const copy_me& other) { std::cout << "Copy initialization of type copy_me" << std::endl; } copy_me& operator=(const copy_me& other) { std::cout << "Copy assignment of type copy_me" << std::endl; return *this; } }; int main() { copy_me object1; copy_me object2 = object1; object1 = object2; return 0; }

In this example, the class copy_me has a copy constructor and a copy assignment operator. The expression copy_me object2 = object1; results in the copy constructor for object2 being invoked. The expression object1 = object2; results in the copy assignment operator for object1 being invoked.

Notice the keyword struct used instead of class. In C++ the only difference between struct and class is that the default member access for struct is public while the default member access for class is private. struct is short for structure and all structures are classes and can also have member functions, constructors, private members, and member operators.

Copy assignment operators should always have one parameter that is a constant reference to an object of the same type and they should always return a reference to the current object being assigned to, which is always *this.

Operator overloading

Creating a user-defined copy assignment operator inside of a class is one example of operator overloading. Other operators can be overloaded, such as the addition operator or the subscript operator.

Let's remake our the card again and instead of using strings, we will use unsigned int to represent the rank and suit:

struct card { unsigned int rank; unsigned int suit; };

If I have two objects a and b of type card, the expression a == b would be invalid unless I overload the "equal to" operator. One way to do that would be to overload it as a member function:

struct card { unsigned int rank; unsigned int suit; bool operator==(const card& other) const { return rank == other.rank && suit == other.suit; } };

You can also overload it as a free function:

struct card { unsigned int rank; unsigned int suit; }; bool operator==(const card& a, const card& b) { return a.rank == b.rank && a.suit == b.suit; }

If I wanted to allow the card to be inserted into an output stream, I can also overload the insertion operator for std::ostream:

std::ostream& operator<<(std::ostream& os, const card& c) { return (os << "card{" << c.rank << ',' << c.suit << '}'); }

Now the expression std::cout << c for some card c is valid.

There are many operators you can overload for a class, not all of them can be overloaded as free functions, some of them can only be overloaded as member functions of the class, and a few operators cannot be overloaded at all.

See http://en.cppreference.com/w/cpp/language/expressions#Operators for a list of operators and their signatures for overloading.