11.1. Overloaded Operators and friend Functions

Time: 00:04:20 | Download: Large, Large (CC), Small | Streaming, Streaming (CC) | Slides (PDF)
Review

Overloaded operators are based on an operator notation but are implemented as functions. Thoroughly understanding operators, functions, and the member function calling syntax is essential for understanding overloaded operators. Please review the following as needed:

Although overloaded operators and friend functions are independent constructs - it is possible to use one without the other - but programmers often use them together. Fortunately, both are simple extensions to C++ features we have already studied and used, making them an easy addition to our programming toolkit.

Overloaded Operators

C++ implements overloaded operators as functions that follow a specific naming convention and calls them with a specialized but familiar operator notation. For example, C++ allows programs to add two integers using the "+" operator: 5 + 10, which looks like the way we would write the operation using "pencil and paper." Similarly, a program can specify a class named fraction with an overloaded "+" operator and sum two instances, f1 and f2, with the expression f1 + f2.

Once you get past the new syntax and a bit of terminology, you'll find that you can apply all your knowledge about functions, both members and non-members, to overloading operators. As a case in point, 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.

C++ implements overloaded operators as functions with the special function name: operator☺ where ☺ is replaced by the overloaded operator. For example, the add function from the Time class example, Time add(Time t); may be restated as 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 the friend keyword that creates functions blurring the line between member and non-member functions:

friend function rules

They:

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 family member, 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.

The definitions and calls of "normal" functions articulate or match closely, making it relatively easy for programmers to understand the calling syntax. Conversely, implementing an overloaded operator as a function makes its definition strikingly different from the operator-form calling syntax. Asking yourself three questions about a given operator and identifying the answers will help you understand it and its behaviors:

  1. To which class does the operator belong? Multiple classes can overload the same operator, and each version will have distinct tasks and behaviors.
  2. Does the program implement the operator as a member or a friend? The distinction affects argument passing and the attribute accessing syntax in the function's body.
  3. Is the operator unary or binary? This distinction affects the number of allowed operands. C++ has one tertiary operator but rarely overloads it.

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 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)