Operator Overloading

Five small topics (“digressions”) we have to get out of the way before we do serious operator overloading.

A digression on inline functions

  1. Good news: the function f occupies very little memory, and takes only a microsecond to execute. Bad news: it takes a microsecond to call (i.e., go down to) f and a microsecond to return from it (i.e., come back up). That means we spend two-thirds of our time on the road.
    1. inline1.C. No inline functions yet.
    2. inline2.C. Let the function f be an inline function.
    3. A class with an inline constructor, an inline member function, and an inline friend function. Compile this three-file program with
      c++ main.C point.C
      
      1. point.h
      2. point.C
      3. main.C

A digression on the “this” pointer

In a member function, the variable this is a pointer that points to the object that the member function belongs to. In other words, it is the invisible pointer that was passed to the member function.

  1. this.C

A digression on prefix (simple) vs. postfix (complicated) increment

  1. prefix.C

A digression on passing a reference as an argument to a function

When we passed the object a1 to the function f, we created a copy of the object.
But when we passed the objects a2 and a3 to the function f, we did not create a copy of the object.
We can do it either way.

The function f receives the objects a2 and a3. f can change a2 but not a3.

  1. reference.C
born holding 10
born holding 20
born holding 30
born holding a copy of another object's 10
The function f has been called.
dying holding 10
We have returned from the function f.
dying holding 30
dying holding 21
dying holding 10

A digression on a missed opportunity to make static data members

Here is jsr1’s three-file program height.h, height.C, main.C. The two variables cm_per_inch and inches_per_foot in the file height.C could have been private static data members like the length private static data member of the class date in date.h, date.C, main.C.

The class date that we’re starting with

  1. The motivation for operator overloading is on p. 271, which is p. 1/77 of the PDF file.
  2. The one-data member class date which we will provide with overloaded operators:
    1. date.h
    2. date.C
    3. main.C
    c++ date.C main.C
    

Overload the expected operators

  1. To let the output operator << take a right operand of class date,
    	date d;
    	cout << d;
    
    	//If we define operator<< to be a friend function, the computer
    	//would behave as if we'd said
    	//operator<<(cout, d);
    
    	//If we define operator<< to be a member function, the computer
    	//would behave as if we'd said
    	//cout.operator<<(d);
    	//This would be of no help in allowing operator<< to mention the
    	//private member(s) of d.
    
    we have to define a function named operator<<. This function needs to mention the private member(s) of class date. Therefore this function must be a member function or a friend of class date. But 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, an object of class ostream. Being a member function of the object cout would get the operator<< function permission to mention the private member(s) of cout, which we do not need, but not the private member(s) of the date. Therefore, operator<< will have to be a friend of class date.

    We have to pass cout and d as arguments to the function operator<<, but we don’t want to create copies of these two objects. We have to let operator<< change the value of cout, but we don’t want to let operator<< change the value of d.

    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;          //operator<<(cout, d);
    	cout << d << "\n";  //operator<<(cout, d) << "\n";
    
    	cout << a;           //                      operator<<(cout, a);
    	cout << a << b;      //           operator<<(operator<<(cout, a), b);
    	cout << a << b << c; //operator<<(operator<<(operator<<(cout, a), b), c);
         	//etc.
    
    1. date.h
    2. date.C
    3. main.C
  2. Let the operator += take a left operand of class date.
    //Why the operator += is simpler than the operator +
    
    	int i {10};
    	i += 10;   //+ merely changes the value of an existing variable.
    
    	//+ creates a new (invisible) int variable, born holding the value 30.
    	cout << i + 10 << "\n";
    

    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. But because it uses 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 function.

    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) << "\n";  //cout << d.operator+=(7) << "\n";
    
    The function operator+= must therefore return the date object. We return this date object without creating a copy of it.

    1. date.h
    2. date.C
    3. main.C
  3. Four operator functions can be implemented by calling the function operator+=. They are two different flavors of operator+, and two different flavors of operator++.
    1. The three files are
      1. date.h
      2. date.C
      3. main.C
    2. To let us say
      	//in the main function
      	date d;
      	date nextWeek {d + 7};   //date nextWeek {operator+(d, 7)};
      	date nextYear {365 + d}; //date nextYear {operator+(365, d)};
      	cout << (d + 7) << "\n"; //cout << operator+(d, 7) << "\n";
      
      define the following two inline functions in date.h, after (not inside of!) the declaration for class date. They do not need to be member functions or friends, because they do not mention any private member of class date.
      };   //the end of the declaration for class date
      
      inline const date operator+(date d, int i) {return d += i;}   //JF of P
      inline const date operator+(int i, date d) {return d += i;}
      
      #endif  //matches the #ifndef DATE_H at the top of date.h
      
      The above two functions return a const date to prevent anyone from saying
      	date d;
      
      	//Okay to use the value of an invisible variable (a "temporary"),
      	cout << (d + 7) << "\n";
      
      	//Not okay to try to change the value of an invisble variable.
      	(d + 7) += 365;
      
      They cannot return a reference to a date, because the object d created when the function is called is automatically destructed when the function returns, and you never want to return a refernce to an object that has already been destructed.
    3. To let us apply a prefix increment to a date object,
      	//in the main function
      	date d;
      	cout << d << "\n";    //today
      
      	++d;
      	cout << d << "\n";    //tomorrow
      
      	cout << ++d << "\n";  //the day after tomorrow
      
      define the following inline function in date.h, after (not inside of!) the declaration for class date. It does not need to be a member function or friend, because it does not mention any private member of class date.
      };   //the end of the declaration for class date
      
      inline date& operator++(date& d) {return d += 1;}   //prefix increment
      
      #endif  //matches the #ifndef DATE_H at the top of date.h
      
    4. To let us apply a postfix increment to a date object,
      	date d;
      	cout << d++ << "\n"; //Outputs today, leaves d holding tomorrow.
      	cout << d << "\n";
      
      define the following inline function in date.h, after (not inside of!) the declaration for class date. It does not need to be a member function or friend, because it does not mention any private member of class date. The only purpose of the second argument (the int) is to allow us to have two functions with the same name. That’s why we didn’t even give a name to the second argument.
      };   //the end of the declaration for class date
      
      inline const date operator++(date& d, int)    //postfix increment
      {
      	const date old {d};
      	++d;   //d.operator++();
      	return old;
      }
      
      #endif  //matches the #ifndef DATE_H at the top of date.h
      
    5. Also implement the operators -=, - (to subtract an int from a date), prefix --, postfix --.
  4. Operators that need to mention the private members of two objects of the same class should be implemented as friend functions ofthe class.

    The operator- that takes two objects should be a friend. Recall that the operator- that took an object and an int was neither a member function nor a friend. (See the analogous operator+ in this date.h.)

    operator== and operator< should be friends. The four other comparison operators (operator!=, operator<=, operator>, operator>=) should do thir work by calling operator== and operator<

    1. date.h
    2. date.C
    3. main.C
  5. Let the input operator >> take a right operand of class date. operator>>, like operator<<, should be a friend function.
    1. date.h
    2. date.C
    3. main.C
  6. Exercise. Make a class grade. An object of this class can contain one of 14 possible values: F, F+, D-, D, D+, C-, C, C+, B-, B, B+, A-, A, A+. Overload all the operators that we just overloaded from class date. What data member(s) would an object of class grade have to contain? Give it a constructor that takes a string as its operand. And give it an operator<< that will output an uppercase letter, optionally followed by a plus or minus.
    	grade good {"A"};
    	grade bad  {"C-"};
    	cout << good << "\n";
    
  7. 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
  8. 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
  9. Define two flavors of operator[] for an object that holds a series of values.
    1. mystring.h
    2. mystring.C
    3. main.C
  10. Define a member function named operator int for class grade.
  11. 	//Declaration and definition for inline member function in grade.h.
    	operator int() const {return i;}
    
    	//Demonstrate operator int in main.C.
    	grade a {"A"};
    	cout << static_cast<int>(a) << "\n";
    
    	//behaves as if you'd written
    	//cout << a.operator int() << "\n";
    
    Let's pretend that the class of the object cin (namely, class istream) has the following functions.
    	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";
    	}