Essentialssec12.htm

Essentials of C++: Section XII


Operator Overloading

One can think of C++ operators as symbols (the technical term is tokens) that trigger some computation. However, exactly what computation is performed is not fixed. It depends on the operands. For example, the addition of two integers requires a different set of machine level instructions than the addition of two doubles. One can therefore talk about the overloading of operators.

Most computer languages do some overloading of operators as part of the language itself. It is possible, however, in C++ for programmers themselves to re-define the meaning of C++ operators as long as at least one of the operands is an instance of a class. Such overloading allows one to, for example, add two dates using the '+' operator or compare two weights (in pounds and ounces) using the relational operators. As with functions, C++ determines which of the computations associated with an operator to use by looking at the number and types of the operands. (Operands are to an operator what parameters are to a function.)

All operators with the exception of:

. the dot (or member selection) operator
:: the scope operator
.* and ?:     not discussed in this text

can be overloaded. However, different operators have different restrictions and you should consult your C++ manuals for details
 

A. Declaring an Overloaded Operator
The general form to declare an operator overload is:

return-type operator the-operator's-symbol (operands as parameters);

The word 'operator' followed by the operator symbol forms what is called the operator function name. Thus, we refer to overloaded operators as function operators or operator functions.

For example, if we assume the existence of a class called 'Date', we could declare the overloaded operator function '+' that adds two instances of 'Date'and returns a Date as:

friend Date operator+ (const Date d1, const Date d2);

The number of arguments (operands) found in an overload declaration depends on whether the operator function is declared as a friend or member function of the class in question. (It must be one of the two in order to have access to the members of the class.) As declared here, the operator function '+' has two explicit operands, 'd1' and 'd2', and it has access to the internals of both of them because it is a friend of 'Date'.

Since member functions have one implicit argument (a pointer to the instance being "sent the message" - representing by the keyword 'this'), member operator functions need one less explicitly declared operand. Thus the declaration for +=, meaning to add some number of days to a date might be:

Date operand+= (int days);

We could also rewrite the declaration for '+' as:

Date operator+ (const Date d2);

In both these cases we only need one declared operand because the other operand is implicit.

When adding two dates using the friend form of the overloaded '+' operator, the order of the operands is of little importance. Once could, for example, declare three dates as follows:

Date date1;
Date date2;
Date date3:

and write:

date3 = date1 + date2;
or
date3 = date2 + date1;

However, if we want to use the overloaded '+' operator to add some number of days to a date, we need to be more careful. By using the declaration:

Date operator+ (int days);

we are saying that '+' is a member operator function and that the integer must be to the right of the '+' symbol since the operand to the left is passed implicitly as a pointer to an instance of 'Date'. When dealing with member operator functions, the left most operand must be an instance of a class. It would be more versatile to rewrite this as two forms:

friend Date operator+ (const Date d1, int days);

friend Date operator+ (int days, const Date d1);

Now one can use this operator with an integer as the left or right operand.

It is important that you carefully analyze the use of any overloaded operators before you begin writing the declaration(s). Some operators:

(),    [],    ->.    =

must be declared as class members. In other cases, the decision belongs to the programmer. Clearly, if it makes sense for the operator to be commutative (so the operands can change order), the operator function should be made a friend. On the other hand, if the operator is always called with the left most operator being a class instance - as in '+=' in the dates example above - the operator function should be declared as a member of the class.

In certain circumstances, the traditional use of the operator determines how it should be declared. For instance, we always write:

cout << some-date;

To accomplish this (to overload the << operator to accept dates as a data type) we must overload the << operator as a friend since the left operand is not an instance of the 'Date' class.

Finally, some experts argue that operator functions that have no side effects should be written as friend functions since they have no effect on the data members anyway.

B. Defining an Overloaded Operator
The code itself for an overloaded operator depends on what the programmer wants to happen. There are no special rules. If the overloaded operator is to be a member function, the scope operator, ::, must be included with the class name. It is also likely that the keyword 'this' will be widely used. As an example, consider the definition:

Date Date :: operand+= (int days)
{
     // code to handle the addition
     return *this;
}

Since this operator returns the data passed in as an implicit parameter after it has been modified , we return *this. Note that *this is the result of dereferencing a pointer to a date. In many cases the return type is declared as 'Date&' to return a reference to the date and avoid a copy operation.

Top of Section Main Menu of Essentials of C++ Next Section