Overview

OPERATOR OVERLOADING

Operator overloading is one of the most exciting features of object-orientated programming. It can transform complex obscure program listings into intuitively obvious ones.

For example we may have created a complex class that deals with some kind of numeric information. If we want to add two objects, we may have to use statements like

There is nothing obscure about the syntax - struct has already been covered, all this is doing is something similar!

d3.AddObjects(d1,d2); // correct but obscure syntax

OR

Equally not obscure!

d3 = d1.AddObjects(d2); // equally obscure.

But using operator overloading, we can change the syntax to something that is much more readable:

I'd say this was not as clear as the above!

d3 = d1 + d2; // much clearer

The term "operator overloading" refers to giving the normal C++ operators such as +, *, <= and += additional meanings when they are applied to user-defined data types.

Well, I'd not say that the language is being refined...

In effect, operator overloading gives you the opportunity to redefine the C++ language. If you find yourself limited by the way C++ operators work, you can change them to do whatever you want. By using classes to create new kinds of variables, and operator overloading to create new definitions for operators, you can extend C++ to be, in many ways, a new language of your own design.

Most importantly though, nothing is said about what you can't overload - by the looks of it, just about anything can be overloaded

Effort must however be taken to use overloaded operators to perform operations that are as similar as possible to those performed on basic data types.






UNARY OPERATOR OVERLOADING

A unary operator is an operator that acts on only one operand. An operand is simply a variable or value that is acted on by an operator. Examples of unary operators are the increment and decrement operators ++, -- and the unary minus, as in -33.

And let's not say why or explain what's going on!

Lets overload the ++ operator:

#include <iostream.h>

class Counter
{
   private:
     unsigned int count;
   public:
     Counter() { count = 0; }
     int get() { return count; }
     operator ++ () ( ++count; }	// ++ operator overloaded.
};

void main()
{
  Counter c1,c2;
  cout << "\nc1 = " << c1.get();
  cout << "\nc2 = " << c2.get();
  ++c1;				// overloaded operator
  ++c2;				// called for all objects
  ++c2;
  cout << "\nc1 = " << c1.get();
  cout << "\nc2 = " << c2.get();
}

Notice that in this case the operator took no arguments because a member function can always access the particular object for which it's been called.

Also notice the statement:

c1 = ++c2;

would not work. This is because we have defined the ++ operator to have a return type of void in the operator ++() function, while in the assignment statement it is being asked to return a variable of type counter.


This could be overcome by modifying the operator ++() function :

Counter operator ++()
{
  ++count;
  Counter temp;
  temp.count = count;
  return temp;
}

In the above the operator ++() function creates a new object of type counter called temp, to use as a return value. It increments the count data in it's own object as before, then creates the new temp object and assigns count in the object the same value as it's own object. Finally, it returns the temp object.

Another way of doing this would be:

Counter operator ++()
{
  ++count;
  return Counter(count);
}

The above statement does the same as all three statements in the previous example. The statement return Counter(count); creates an object of type counter. This object has no name, it won't be around long enough to need one. Thiis unnamed object is initialised to the value provided by the argument count.

We must however make one change to our program, as the statement now needs a constructor that takes one argument:

Counter int(c) { count = c; }

Once the unnamed object is initialised to the value of count it can then be returned.

If this seems a little familiar, it should. We have used this technique before with function overloading. The new constructor does not replace the old one, but is in addition to. Not suprisingly the new constructor is called an overloaded constructor.


BINARY OPERATOR OVERLOADING

Lets look again at the measurements program. We could write a member function to add the measurements together:

dist3.add(dist1,dist2);

But by overloading the + operator we could change the expression to

dist3 = dist1 + dist2;

Although this appears complex at first, careful study will increase understanding of the methods used.

First the class prototype. The syntax, is as before only this time we need to return a value of type Distance. Therefore, the syntax becomes Distance operator + (Distance d2);

The format being, first the return type Distance, followed by the keyword operator followed by the operator to be overloaded, in this case + and lastly the arguments (Distance d2);

Now for the overloaded operator function. Thie lines are numbers to match the comments which follow.

  • Distance Distance::operator + (Distance d2)
  • int ft = feet + d2.feet;
  • float in = inches + d2.inches;
if (in >= 12.0)
{
  in -= 12.0;
  ft++;
}
  • return Distance (ft,in);


Comments:

Line 1 : Distance Distance :: operator + (Distance d2)

The format of this line is :

The return type Distance,

The class, with the scope resolution operator and function name Distance::operator +

And finally the arguments (Distance d2).

Line 2 : int ft = feet + d2.feet;

Line 3 : float in = inches + d2.inches;

These lines declare an int variable called ft and a float variable called in. They are the assigned the result of the addition of feet and d2.feet and inches and d2.inches respectively.

Because the function we are writing is a member of the class and function will be called for an individual object, the private data of that object i.e. feet and inches is of course available. We can therefore add the calling object's private data, feet and inches to the object d2 feet and inches that have been passed in the function argument.

Line 4 : return Distance(ft,in);

This line makes a temporary nameless object, of class Distance, initialises it with the results of ft and in and returns the temporary object to the calling function.

We now need to make an overloaded constructor that will initialise an object with feet and inches. First the class prototype:

Distance (int ft, float in);

Then the function

Distance (int ft, float in)
{
  feet = ft;
  inches = in;
}

Finally add the following lines to main()

  dist3 = dist1 + dist2;
  cout << "Distance 3 measures ";
  dist3.show();

For completeness there now follows a complete program listing. The last modifications are in bold type:

iostream.h and conio.h - doesn't exactly fill you with confidence!

#include <iostream.h>
#include <conio.h>

class Distance
{
  private:
    int feet;
    float inches;
  public:
  // Constructor prototypes
    Distance();
    Distance(int ft, float in);
  // Destructor prototypes
    ~Distance();
  // Overloaded operator prototype
    Distance operator + (Distance);
  // Ordinary member function prototypes
    void set(int ft, float in);
    void get();
    void show();
};

/****************Default onstructor function***************/

Distance::Distance()
{
   feet = 0;
   inches = 0;
}

/****************Overloaded constructor function***********/

Distance::Distance(int ft, float in)
{
  feet = ft;
  inches = in;
}

/***********Overloaded operator function***********/

Distance Distance::operator + (Distance d2)
{
  int ft = feet + d2.feet;
  float in = inches + d2.inches;
  if (in >= 12.0)
  {
    in -= 12.0;
    ft++;
  }
  return Distance(ft,in);
}

***************Destructor function****************/

Distance::~Distance()
{
}

/****************Functions*********************/

void Distance::set(int ft, float in)
{
   feet = ft;
   inches = in;
}

void Distance::get()
{
  cout << "Please input value for feet\n";
  cin >> feet;
  cout << "Please input value for inches\n";
  cin >> inches;
}
void Distance::show()
{
  cout << feet << " feet " << inches << " inches\n";
}

void main()
{
  clrscr();
  Distance dist1,dist2,dist3;
  dist1.set(11,6.25);
  dist2.get();
  cout << "Distance 1 measures ";
  dist1.show();
  cout << "Distance 2 measures ";
  dist2.show();
  dist3 = dist1 + dist2;
  cout << "Distance 3 measures ";
  dist3.show();
  getch();
}