next up previous
Next: About this document ...

CSCI.1200 - Computer Science II
Summer, 2002
Worksheet 9
Operator Overloading

Reading: Deitel & Deitel, Chap. 8

Note: You may run into bizzare Microsoft compilation errors while doing the examples in this worksheet. If you do, please see the email Paul sent out regarding fixes and workarounds for Worksheet 9 (the text of the email is pasted here)

When you define your own types using classes, you often want to write code which allows you to use C++ operators on them. You might want to add two objects together using the + operator or to output them using the << operator. This is called operator overloading, because you are taking a previously defined operator and giving it a new meaning.

In fact, most of the operators that we commonly use are already overloaded, in the sense that they have different meanings depending on the type of their operands. For example, the + operator performs a different operation if its operands are of type double than if they are of type int.

Note that all operators have arguments (they are typically called operands rather than arguments, but the meaning is the same) and they all return a value. Thus, overloading an operator is like writing a function. For example, the plus operator takes two operands and returns a third operand, so overloading the plus operator to add two instances of the class X which returns an instance of the class X is exactly the same as writing a function with this signature:
X Add(X x1, X x2)

There are two ways to overload an operator. You can overload the operator with a member function definition, in which case the current class instance (pointed by the this pointer) is an implicit argument, just as with other member functions. The second way is to overload the operator with a function definition outside the class and declare that function to be a friend, using a friend declaration within the class.

The syntax of operator overloading is as follows:
type operator operator (argument list ) { statements }

Here is a simple example which overloads the + operator for the class Point using the friend method. This allows us to add two points together to get a third point where the x value of the new point is the sum of the two x values and the y value of the new point is the sum of the y values.

#include <iostream.h>
class Point {
  private:
    double x,y;
  public:
    Point() { x = y = 0.0;} // default constructor
    Point(double a, double b){x=a; y=b;} // another constructor
    friend Point operator+(Point, Point);
};

Point operator+(Point a, Point b)
{
   Point temp(a.x + b.x, a.y + b.y); // calls the constructor
   return temp;
}
    
int main()
{
   Point p1(3.4,5.6);
   Point p2(7.8,9.0);
   Point p3 = p1 + p2; 
   return 0;
}
Note that when the overloading is done as a friend function, all class instance arguments must be written explicitly in the argument list.

Thus, overloading an operator is just like defining any other member or friend function definition except for two syntax differences:

If you have difficulty understanding this syntax, you might think how you would do the above definition of addition of points as a function with an ordinary name such as Plus
Point Add(Point a, Point b)
{
   Point temp(a.x + b.x, a.y + b.y); // calls the constructor
   return temp;
}

This definition is equivalent to the operator overloading definition, except for the way the function is named and called. You would have to have write a call, such as the one for adding p1 and p2, as
   Point p3 = Plus(p1, p2);

The second method of overloading an operator is to define it within the class. In this case, the instance of the class is the first, implicit, operand and so there is one fewer argument.
#include <iostream.h>
class Point {
  private:
    double x,y;
  public:
    Point() { x = y = 0.0;}
    Point(double a, double b){x=a; y=b;} // another constructor
    Point operator+(Point n) { // note that there is only one argument
          Point temp(x + n.x, y + n.y);
          return temp;
      }
};

int main()
{
   Point p1(3.4,5.6);
   Point p2(7.8,9.0);
   Point p3 = p1 + p2;
   return 0;
}

These two programs are pretty boring because they don't do anything. It would be nice to be able to output the contents of a Point but if we added following line as the last statement of main,
cout << p3 << endl;
the compiler would generate the an error message, something like:
Error: binary '«': no operator defined which takes a right-hand operand of type class Point.
In other words, the << operator is not defined for an object of type Point. One simple solution to this would be to define a member function PrintPoint() of the Point class as follows:
void PrintPoint() {
   cout << "X is " <<  x << " Y is " << y << endl; 
}
This is what we've done up to this point. However, a better solution is to overload the << operator so that it knows how to output a Point. The << operator is a binary operator, which means that it takes two operands. The second operand would be a Point, but what is the first? The left side of the << can be cout, or an instance of the class ofstream (file open for output), among other things. Both of these are derived classes from the base class ostream,1 an output stream. Thus the left operand of the << operator should be of type ostream. There are two other things that you must do to write this function. The ostream must be a reference pointer, and the operator must return a reference pointer to an ostream.

Here is the solution:

class Point {
  ...
  friend ostream& operator<<(ostream &, Point);
};  // end of definition of the class Point

ostream& operator<<(ostream &os, Point p) {
    os << "X is " << p.x << " Y is " << p.y ;
    return os;
}

Overloading the >> operator is similar except that the first operand is of type istream, and the second argument must be a reference argument because its value is changed.

class Point {
   ...
   friend istream& operator>>(istream is&, Point&);
};  // end of definition of the class Point

istream& operator>>(istream &is, Point& p)
{
    is >> p.x >> p.y;
    return is;
}
Note that the code for overloading the >> and << operators must be done outside the class using the friend method because when operators are overloaded inside the class, the instance of the class is the first operand, and in these cases the first operand is not the same type as the class.

Exercise 1: Define a class IntArray which has one private member, an array of ten ints. The class should have a single constructor, which takes no arguments and sets all ten values in the array to zero. Define a public member function void Setval(int pos, int val) which sets the value of the array at position pos to val (Note that pos must be in the range 0 .. 9).

Define three operators on IntArray

ostream& << IntArray which displays all of ten values on the terminal on a single line separated by a space.

IntArray + (IntArray a1, IntArray a2) which returns a new IntArray whose values at each of the ten positions are the sum of the values of the two arguments at the same position. For example, if the value at position 2 of a1 was 17 and the value of position 2 of a2 was 5, the value of position 2 in the array which was returned would be 22.

IntArray - (IntArray a1, IntArray a2) which returns a new IntArray whose values at each of the ten positions are the value of the first argument minus the value of the second argument. For example, if the value at position 2 of a1 was 17 and the value of position 2 of a2 was 5, the value of position 2 in the array which was returned would be 12.

Here is a short main to test your code:

#include <iostream>
using namespace std;

int main()
{
  IntArray A, B;
  A.setval(0,5);
  A.setval(1,7);
  A.setval(2.23);
  B.setval(0,3);
  B.setval(1,11);
  B.setval(2,10);
  IntArray C = A + B;
  cout << C << endl;
  // should print 8 18 33 0 0 0 0 0 0 0
  C = A - B;
  cout << C << endl;
  // should print 2 -4 13 0 0 0 0 0 0 0
  return 0;
}

Exercise 2 Rewrite the same code defining the last two operations as members of the class.

Any operator which is already defined in C++ can be overloaded with the following restrictions and exceptions.

Overloading the ++ and -- operators

Unary operators (i.e., operators which only take one argument) can be overloaded in the same fashion. Recall that for integers the ++ operator takes two forms, the prefix form and the postfix form. Both increment their operands by 1, but the prefix form returns the incremented value while the postfix form returns the value before incrementing. Here is some sample code for integers to refresh your memory on this:

int i, j;
i = 7;
j = i++;  // postfix
cout << i << ' ' << j << endl; // prints 8 7
i = 7;
j = ++i;  // prefix
cout << i << ' ' << j << endl; // prints 8 8
When we overload ++ for some new type, we should define both a prefix and a postfix version, and their meanings should follow the same conventions as for the built-in integer versions: they should both increment the object they are applied to (in some sense), and the prefix form should return the incremented value while the postfix form returns the value before incrementing.

Here is code to overload the two versions of the ++ operator for the class Point.

class Point {
   ...
    friend Point& operator++(Point&);   // prefix
    friend Point operator++(Point&, int); // postfix 
   ...
};

Point& operator++(Point& p)  // prefix
{
    ++p.x;
    ++p.y;
    return p;
}

Point operator++(Point& p, int) // postfix
{
    Point temp = p;
    ++p.x;
    ++p.y;
    return temp;
}
There are several non-obvious things to notice about these definitions:

The decrement operator -- is overloaded in the same way.

Exercise 3 Add the following additional operators to your class IntArray:
>>, ++ (prefix), ++ (postfix)

To increment an IntArray, add 1 to each of the ten values.




next up previous
Next: About this document ...
Paul Lalli 2002-05-17