Operator Overloading Basics

Fundamentals

Some rules regarding operator overloading

friend functions vs. member functions

Format


Motivation

Example 1

Example 2


Overloading the Arithmetic operators

The arithmetic operators can be overloaded either as stand-alone functions or as member functions.

Overloading as a friend function


Overloading an operator as a member function

Some other possible arithmetic operators


Overloading comparison operators


Overloading the insertion << and extraction >> operators:

As with other operators, the << and >> operators are defined for the basic types.  If you build your own class, don't expect << to automatically work with your new types of objects!   If you want it to work, you have to teach the computer how to do such output.  Consider the following:
 Fraction f; 
 cout << f;        // how would the machine know how to do this? 

We have no reason to expect the second line to work!  The insertion operator << is only pre-defined for built-in types.  The iostream.h library doesn't know about the Fraction type.

The << operator is a binary operator (2 parameters, left side and right side).  The first parameter is always an ostream object (we've mostly used cout, so far).  Because of this, it cannot be defined as a member function (it would have to be a member of the ostream class, which we cannot change).  The << and >> operators should always be defined as outside functions (usually friend functions).  The second parameter is whatever new type it is being overloaded to print:

 friend ostream& operator << (ostream& s, Fraction f); 

This declaration has all of the usual parts for defining a function.  The name is operator<< (the keyword operator and the operator symbol). The return type is ostream&. The parameters are (ostream& s, Fraction f). When defining overloads of << and >> , always pass the stream parameters by reference. A better way to write this operator is:

 friend ostream& operator << (ostream& s, const Fraction& f); 

Notice that the first one passes the Fraction by value (and makes a copy). The second passes by reference (avoiding the overhead of a copy). It is declared as a const because the Fraction does not need to change if we are just doing output.

Here is the corresponding prototype for extraction >>

 friend istream& operator >> (istream& s, Fraction& f);

Notice that the Fraction parameter for >> is also a reference parameter.  This is because we are getting input into the object, so we need to work on the original, not a copy.

Remember the Show() function of the Fraction class?

 void Fraction::Show() 
 { 
   cout << numerator << '/' << denominator; 
 } 

Here is how the << operator might be defined for Fraction.  Notice how similar it is to the Show() function.

 ostream& operator << (ostream& s, const Fraction& f) 
 { 
   s << f.numerator << '/' << f.denominator; 
   return s; 
 } 

Note the differences between this and the Show() function.


Once this is defined, we can use a Fraction object in a cout statement:

  Fraction f1; 

So now, instead of:

  cout << "Fraction f1 is "; 
  f1.Show(); 
  cout << '\n'; 

We can write:

  cout << "Fraction f1 is " << f1 << '\n';

Click here to see the Fraction class with the << overload used instead of Show().

Now, what would the definition of the >> overload look like for Fraction?  Try it!


Another class example

class Complex

This example contains a Complex number class, for implementing complex numbers, of the form a+bi, where a is the real part, b is the imaginary part, and i is the square root of -1. The example illustrates the following operators:

The example also illustrates other concepts we've seen in classes: There is also a sample starter test program. It does not yet test all features of the class, but I will be adding to it soon.