Operator Overloading

A motivation for operator overloading

Later in the course we will have another motivation for operator overloading: it will allow our templates to be applicable to the data types we invent.

//column 1
#include <iostream>
#include "date.h"
using namespace std;

date d;
d.next();
d.next(280);

date e {d};
e.next(7);

int n {distance(e, d)};
date midpoint {d};
midpoint.next(n/2);

if (equals(d, e)) {

d.print();

d.next();
d.print();

d.print();
cout << "\n";

d.next();
d.print();
cout << "\n";
//column 2
#include <iostream>
#include "date.h"
using namespace std;

date d;
++d;
d += 280;

const date e {d + 7};


int n {e - d};
const date midpoint {d + n/2};


if (d == e) {

cout << d;

cout << ++d;


cout << d << "\n";


cout << ++d << "\n";


//column 3
#include <iostream>
#include "date.h"
using namespace std;

date d;
operator++(d);
d.operator+=(280);

const date e {operator+(d, 7)};


int n {operator-(e, d)};
const date midpoint {operator+(d, n/2};


if (operator==(d, e)) {

operator<<(cout, d);

operator<<(cout, operator++(d));


operator<<(operator<<(cout, d), "\n");


operator<<(operator<<(cout, operator++(d)), "\n");


Three big ideas

Three decisions

The class date that we’re starting with

  1. The one-data member class date which we will provide with overloaded operators:
    1. date.h
    2. date.C
    3. main.C, main.txt,
    c++ date.C main.C
    ls -l a.out
    ./a.out
    

Overload the output operator <<

  1. The output operator << already accepts an object of class ostream (such as the object cout) as its left operand. We will now let the operator accept an object of class date as its right operand:
    #include <iostream>  //for the object cout
    #include "date.h"    //for class date
    using namespace std; //because cout belongs to namespace std
    
    	date d;      //Call the default constructor.
    	cout << d;   //cout is the left operand, d is the right operand.
    
    We will have to create a function named operator<<.
    1. This function will need to mention the private member(s) of class date. Therefore this function must be a member function or a friend of class date.

      But our operator<< cannot be a member function of class date. In C++, when an operator is implemented as a member function, it always has to be a member function of its left operand. In this case, the left operand of << is cout. Therefore, in order to be able to mention the private member(s) of class date, operator<< will have to be a friend of class date. When we write the above expression

      	cout << d;
      
      the computer will behave as if we had called the following two-argument friend function of class date.

      	operator<<(cout, d);
      
    2. When we pass cout and d as arguments to the function operator<<, we don’t need to create copies of these two objects. Therefore both arguments will be passed by reference.

      Our operator<< function will perform output, and the act of peforming output changes the value of the object cout. Therefore cout will have to be passed to operator<< as a non-const reference.

      We want to guarantee that we can output a date object without changing or damaging it, so the date object will have to be passed to operator<< as a const reference.

    3. Finally, the value of the expression cout << d must be cout, so that we can use the expression cout << d as part of a larger expression.

              cout << d;
      
             cout << d << "\n";
      
      
    1. date.h
    2. date.C
    3. main.C, main.txt

Overload the += operator

  1. The operator += already accepts an int such as 7 as its right operand. We will now let the operator accept an object of class date as its left operand:
    #include <iostream>
    #inlcude "date.h"
    using namespace std;
    
    	date d;   //Call the default constructor.
    
    	d += 7;   //Change d to the date that's a week from today.
    	          //d is the left operand, 7 is the right operand.
    
    	cout << d << "\n";
    
    	//Why the operator += is simpler than the operator +
    
    	int i {10};
    	i += 10;   //This += merely changes the value of an existing variable.
    
    	//This + creates a new (invisible) int variable, born holding 30.
    	cout << i + 10 << "\n";
    
    1. The function operator+= needs to mention the private members of class date. Therefore it must be either a member function or a friend function of class date. It could be a member function of class date, because this time the date object is the left operand of the +=. And since the operator+= function will use the private members of only one object of the class, we will make it a member function of class date. It will be short enough to be an inline member function.
    2. The value of the expression d += 7 must be the new value of d, so that we can use the expression d += 7 as part of a larger expression.
      
             cout << (d += 7);   //Parentheses force the += to execute before the <<
      
      
      The function operator+= must therefore return the date object that the function belongs to. Unsurprisingly, this date object is *this. To return this date object without creating a copy of it, we return it by reference.
    1. date.h
    2. date.C
    3. main.C, main.txt

Overload the + operator (in two different flavors)

Four more operator functions can be easily implemented by calling the inline member function operator+= that we just wrote. These four functions are two different “flavors” of operator+, and two different flavors of operator++.

  1. The operator + already accepts operands of lots of different combinations of types.
    We will now let the operator accept a date object as its left operand and an int as its right operand.
    We will also let the operator accept an int as its left operand and a date object as its right operand.
    #include <iostream>
    #inlcude "date.h"
    using namespace std;
    
    	date d;
    
    	date nextWeek {d + 7};   //left operand is date, right operand is int
    	date nextYear {365 + d}; //left operand is int, right operand is date
    
    	cout << (d + 14);  //Can also use d + 14 in a larger expression.
    
    
    1. date.h contains four definitions of the function operator+, but the first two are for pedagogical purposes only. Our first pedagogical example mentions the private data member day, so it has to be a member function or friend. I made it a member function, because it uses only one object of class date.

      Our second pedagogical example does its work without mentioning the name of any private member of the class. Therefore this function does not need to be a member function or a friend. It’s still a member function, but unnecesarily so.

      Our last two definitions of operator+ are neither member functions or friends, and are therefore defined outside and below the {curly braces} of the class definition in date.h. An inline function is automatically static, so this .h file can be #included in multipe .C files of a C++ program without incurrin the “multiple definition” error.

    2. Remember, our operator+ function must create and return a new object of class date. The pedagogical examples create a new object by explicitly creating a copy of the object they belong to. The non-pedagogical examples create an object in an easier way, simply by using pass-by-value to create a copy of the object that was passed to them.
    3. operator+ returns the new object it created. Unfortunately, we must create and return a copy of this new object, because the new object dies when the operator+ function returns. To make sure that no one tries to change the new object, the return value is a const object:
      	date d;
      
      	//Can use the value of the new object returned by operator+
      	//(This invisible object is called a "temporary".)
      	cout << (d + 7) << "\n";
      
      	//but cannot change the value of the new object returned by operator+
      	(d + 7) += 365;
      
    The program that demonstrates operator+ is
    1. date.h
    2. date.C
    3. main.C main.txt

Overload the prefix ++ operator

  1. The prefix ++ operator already accepts an operand of type int (and other types too). We will now let the operator accept an operand of type date.
    #include <iostream>
    #include "date.h"
    using namespace std;
    
            date d;
            cout << d << "\n";    //today
    
            ++d;
            cout << d << "\n";    //tomorrow
    
           cout << ++d << "\n"; //the day after tomorrow
    
    
    1. date.h contains three definitions of the function operator++, but the first two are for pedagogical purposes only. Our first pedagogical example mentions the private data member day, so it has to be a member function or friend. I made it a member function, because it uses only one object of class date.

      Our second pedagogical example does its work without mentioning the name of any private member of the class. Therefore this function does not need to be a member function or a friend. It’s still a member function, but unnecesarily so.

      Our last definition of operator++ is neither a member function nor a friend, and is therefore defined outside and below the {curly braces} of the class definition in date.h, just as we did for the final definitions of operator+.

    2. Prefix operator++, like operator+=, does not create any new object. It merely modifies the value of an ixisting object, and then returns the existing object without making a copy of it.
    The program that demonstrates prefix operator++ is
    1. date.h
    2. date.C
    3. main.C, main.txt

Overload the postfix ++ operator

  1. The postfix ++ operator already accepts an operand of type int (and other types too). We will now let the operator accept an operand of type date.
    #include <iostream>
    #include "data.h"
    using namespace std;
    
            date d;   //Default constructor puts today into the newborn object.
    
           cout << d++ << "\n"; //Outputs today, leaves d holding tomorrow.
    
            cout << d   << "\n"; //Outputs tomorrow.
    
    1. date.h contains three definitions of the function for postix operator++, but the first two are for pedagogical purposes only. Our first pedagogical example mentions the private data member day, so it has to be a member function or friend. I made it a member function, because it uses only one object of class date.

      Our second pedagogical example does its work without mentioning the name of any private member of the class. Therefore this function does not need to be a member function or a friend. It’s still a member function, but unnecesarily so.

      Our last definition of operator++ is neither a member function nor a friend, and is therefore defined outside and below the {curly braces} of the class definition in date.h, just as we did for the final definition of prefix operator++.

    2. Remember, our postfix operator++ function must create and return a new object of class date. The pedagogical examples create a new object by explicitly creating a copy of the object they belong to. The non-pedagogical example creates an object in an easier way, simply by using pass-by-value to create a copy of the object that was passed to it.
    3. Like operator+, the postfix operator++ returns the new object it created. Unfortunately, we must create and return a copy of this new object, because the new object dies when the postfix operator++ function returns. To make sure that no one tries to change the new object, the return value is a const object:
      	date d;
      
      	//Okay to use the vale of the expression d++
      	cout << d++ << "\n";
      
      	//But not okay to try to change the value of the expressio d++
      	(d++) += 7;
      
    The program that demonstrates postfix operator++ is
    1. date.h
    2. date.C
    3. main.C, main.txt

The story so far

  1. Exercise.
    Write one version of class date equipped with all the operators we have overloaded so far. Also add the four subtraction operators -=, -, prefix --, and postfix --, corresponding to the four addition operators +=, +, prefix ++, and postfix ++.

Friends that use two objects of the same class

A function that need to mention the private members of two (or more) objects of the same class should be a friend function of the class.

  1. Let’s create another operator-.

    operator== and operator< should also be friends. The four other comparison operators (operator!=, operator<=, operator>, operator>=) do not need to be member functions or friends, because they can do their work by calling operator== and operator<.

    Why does C++ allow us to make our own definition of what == means? “Close enough for government work.”

    1. date.h
    2. date.C
    3. main.C, main.txt

Overload the input operator >>

  1. The input operator >> already accepts an object of class istream (such as the object cin) as its left operand. We will now let the operator accept an object of class date as its right operand. The function operator>> has to be a friend for the same reason that the function operator<< had to be a friend.

    The operator<< for class date had to perform five separate output operations: int, char, int, char, int. Similarly, the operator>> for class date has to perform five separate input operations: int, char, int, char, int. The two chars should be diagonal slashes. If the chars are the wrong chars, or if the ints are the wrong ints, we call setstate to inject cin with a dye marking it as unhealthy.

    1. date.h
    2. date.C
    3. main.C, main.txt

Overload the conversion operators static_cast<int>(), etc.

  1. Quick review of the static_cast<int>() operator.
    1. cast.C, cast.txt
  2. An object of class grade can hold one of 14 possible values:
    F
    F+
    D-
    D
    D+
    C-
    C
    C+
    B-
    B
    B+
    A-
    A
    A+
    
    1. grade.h
    2. grade.C
    3. main.C, main.txt

    Class grade already has a few operators. Give it all the operators we gave to class date.

    operator+ (object + int)
    operator+ (int + object)
    
    operator++ (prefix)
    operator++ (postfix)
    
    operator-  (object - int)
    operator-  (object - object)
    
    operator==
    operator<
    operator<=
    operator>
    operator>=
    operator!=
    

    In the for loop in main.C, note that we are now able to write the grade object a[i] in a situation (after the +=) where normally we would have to write an int.

A class that already has the conversion operator static_cast<bool>()

  1. The class of the object cin (namely, class istream) has the following functions.
    	int i {0};
    	cout << "Please type an int (or control-d to exit): ";
    	cin >> i;         //operator>>(cin, i);
    
    	if (cin) {        //if (cin.operator bool()) {
    		cout << "The input was successful.\n";
    	}
    
    	if (!cin) {       //if (cin.operator!()) {
    		cout << "The input was not successful.\n";
    	}
    

Copy constructor vs. the operator= member function

  1. The following class point doesn’t need any point.C implementation file, because all the member functions and friends of this class are inline, and the class has no static data members. Class point has a “copy constructor” and an operator= member function. The two functions seem similar, and they even take exactly the same argument (const point& another). But we can already see one difference between them: operator= ends with return *this;, just like operator+= and operator-=. This allows the following y = x to be used as part of a larger expression.
    	point x;
    	point y;
    	point z;
    
    	z = y = x;
    
    

    Even if you never define the copy constructor and the operator= member function shown in point.h, the computer would behave as if you had defined them.

    1. point.h
    2. main.C, main.txt
    So when would you need to define a copy constructor and an operator=?
  2. Quick review of "double quoted" strings in C++.
    1. string.C, string.txt.
  3. A copy constructor is usually simpler than an operator=. An operator= usually has to do the work of the destructor, followed by the work of the copy constructor, checking if we really have two separate objects, and it must return the object *this. (Digression: see string.C for a string of chars in "double quotes".)
    1. mystring.h
    2. mystring.C
    3. main.C, main.txt

    The operator= member function in the above mystring.C has three features that the copy constructor lacks:

    The rule of the Big Three:
    If you have to write any of the following for your class, you probably have to write all three of them:

Overload the ( ) operator

  1. An object of class myrandom does only one job: providing a random number. In other words, other than the constructor and destructor, an object of this class has only one member function (rand) that we will call. In this case, we should have named the member function operator().
    1. myrandom.h
    2. myrandom.C
    3. main.C, main.txt
  2. An example of operator overloading. The for loop in main.C is made out of familiar operators such as >, <<, and --. Compare it with the while loop in interesting.C.
    1. interesting.C by fkhan67
    2. timer.h
    3. timer.C because one of the member functions was too big to be inline
    4. main.C
    c++ timer.C main.C