11.1. Overloaded Operators and friend Functions

Time: 00:06:13 | Download: Large Small | Streaming | Slides (PDF)
Review

Overloaded operators are based on an operator notation but are implemented as functions. A thorough understanding of operators, functions, and how class member functions are called is essential for understanding overloaded operators. Please review the following as needed:

Although overloaded operators and friend functions are independent constructs, meaning that it is possible to use one without the other, programmers often use them together. Java does not support either, so the concepts may be new for many students. Fortunately, both overloaded operators and friend functions are simple extensions to parts of C++ that we have already studied and used, making them an easy addition to our programming toolkit.

Overloaded Operators

Overloaded operators are just functions dressed up with a bit of new syntax called with a specialized but familiar operator notation. For example, C++ allows programmers to add two integers using the "+" operator: 5 + 10, which looks like the way we would write the operation using "pencil and paper." Similarly, we can write code that uses operators to call functions by overloading the operators for programmer-defined classes. For example, if f1 and f2 are instances of a class named fraction, their sum can be written with an overloaded "+" operator as: f1 + f2, which looks like what we might write the operation on a whiteboard or a piece of paper.

Once you get past the new syntax and a bit of terminology, you'll find that you can apply all of your knowledge about functions, both members and non-members, to overloading operators. We have been using overloaded operators all semester but haven't called them that. The inserter, <<, and the extractor, >>, operators were originally used in C (and can still be used in C++) as the left and right bit-shift operators. C++ overloads (i.e., reuses) these two operators in the context of streams as the output and input operators, respectively.

Overloaded operators are implemented as functions with a special function name: operator☺ where ☺ is replaced by the operator to be overloaded. For example, the add function from the Time class example, Time add(Time t); may be transformed into the overloaded operator Time operator+(Time t);. There are a few basic characteristics that all C++ operators share and that overloaded operators cannot change:

Overloaded operator rules

When creating new overloaded operators, programmers cannot

Additionally, overloaded operators should be intuitive, that is, used in a natural way. (For example, if you create a class called Number, you shouldn't define "+" to perform subtraction for two instances of the Number class.) However, what seems intuitive for one programmer may not be so intuitive for another - some have argued that << and >> are not "intuitive" I/O operators. Nevertheless, if you keep that ultimate goal in sight, your overloaded operators will be better in the long run and should be acceptable to other programmers.

friend Functions

This chapter also introduces a new keyword: friend. friend functions blur the line between member and non-member functions:

friend function rules

friend functions

class Time
{
	private:
		int	hours;
		int	minutes;
		int	seconds;

	public:
		Time(int h, int m, int s);
		Time(int s);
		friend	Time	add(Time t1, Time t2);
};
Declaring a function as a friend. The friend keyword only appears in the class specification. This approach prevents arbitrary functions from becoming friends and accessing a class's private features.

Friend functions are like a trusted neighbor: they are not a member of our family, but we trust them with keys that allow them to enter our (private) house. By trusting our friends, they can carry out tasks on our behalf (e.g., bringing our mail while we are on vacation) that they could not do otherwise. Similarly, friend functions can perform tasks that not even member functions can do.

While overloaded operators are relatively simple, focusing on four concepts will help you understand them more quickly and deeply.

  1. Make sure you always know if a description or an example of an overloaded operator is implemented as a member or a non-member (i.e., as a friend ) function
  2. Make sure you know how the operator's operands are translated into the implementing function's arguments; the translation is influenced by binary operators vs unary operators and by member vs non-member (i.e., friend ) functions
  3. Keep in mind that the function definition/prototype looks quite different than does the function call (they are still related, just as they are for regular functions, but the call generally looks quite different than the definition)
  4. Make sure you know for which class an operator is overloaded as that determines which overloaded function is ultimately called

These concepts are described and illustrated in the following sections.

Summary

    Number Of Implicit Arguments Number Of Explicit Arguments Total Number Of Arguments
Unary Member 1 0 1
friend 0 1 1
Binary Member 1 1 2
friend 0 2 2

The relationship between operator operands and function arguments. Strictly speaking, function definitions have parameters, function calls have arguments, and operators have operands:
  1. function(param1, param2, ...) { ... }
  2. op1 + op2
When programmers create new overloaded operators in C++ programs, they implement each operator as a function but call the operator function using operator syntax. When defined, each operator operand corresponds to one function parameter; when called, each operator operand corresponds to one function argument.
Unary Operators
Require one operand: *x
Binary Operators
Require two operands: x * y
Implicit Arguments
Always appear before the dot operator in the function call and are bound to the function call with the this pointer: x.function(y); only member functions have implicit arguments
Explicit Arguments
Always appear in the argument list, that is, inside the of the parentheses: function(x,y)
Member Functions
Are members of or part of a class and always have one implicit argument
friend (i.e. non-member) Functions
Are not members of the friending class; nevertheless, they have access to the non-public features of the class; they do not have an implicit argument so all arguments are explicit (i.e., in the argument list, between parentheses)