The Second Version of Overloading <<
The implementation just presented has a problem. Statements such as this work fine:
cout << trip;
But the implementation doesn’t allow you to combine the redefined << operator with the ones cout normally uses:
cout << "Trip time: " << trip << " (Tuesday)\n"; // can't do
To understand why this doesn’t work and what must be done to make it work, you first need to know a bit more about how cout operates. Consider the following statements:
int x = 5;
int y = 8;
cout << x << y;
C++ reads the output statement from left to right, meaning it is equivalent to the following:
(cout << x) << y;
The << operator, as defined in iostream, takes an ostream object to its left. Clearly, the expression cout << x satisfies that requirement because cout is an ostream object. But the output statement also requires that the whole expression (cout << x) be a type ostream object because that expression is to the left of << y. Therefore, the ostream class implements the operator<<() function so that it returns a reference to an ostream object. In particular, it returns a reference to the invoking object—cout, in this case. Thus, the expression (cout << x) is itself the ostream object cout, and it can be used to the left of the << operator.
You can take the same approach with the friend function. You just revise the operator<<() function so that it returns a reference to an ostream object:
ostream & operator<<(ostream & os, const Time & t)
{
os << t.hours << " hours, " << t.minutes << " minutes";
return os;
}
Note that the return type is ostream &. Recall that this means that the function returns a reference to an ostream object. Because a program passes an object reference to the function to begin with, the net effect is that the function’s return value is just the object passed to it. That is, the statement
cout << trip;
becomes the following function call:
operator<<(cout, trip);
And that call returns the cout object. So now the following statement does work:
cout << "Trip time: " << trip << " (Tuesday)\n"; // can do
Let’s break this into separate steps to see how it works. First, the following invokes the particular ostream definition of << that displays a string and returns the cout object:
cout << "Trip time: "
So the expression cout << "Trip time: " displays the string and then is replaced by its return value, cout. This reduces the original statement to the following one:
cout << trip << " (Tuesday)\n";
Next, the program uses the Time declaration of << to display the trip values and to return the cout object again. This reduces the statement to the following:
cout << " (Tuesday)\n";
The program now finishes up by using the ostream definition of << for strings to display the final string.
As a point of interest, this version of operator<<() also can be used for file output:
#include
...
ofstream fout;
fout.open("savetime.txt");
Time trip(12, 40);
fout << trip;
The last statement becomes this:
operator<<(fout, trip);
And as Chapter 8 points out, the properties of class inheritance allow an ostream reference to refer to ostream objects and to ofstream objects.
Tip
In general, to overload the << operator to display an object of class
ostream & operator<<(ostream & os, const
{
os << ... ; // display object contents
return os;
}
Listing 11.10 shows the class definition as modified to include the two friend functions operator*() and operator<<(). It implements the first of these as an inline function because the code is so short. (When the definition is also the prototype, as in this case, you use the friend prefix.)
Caution
You use the friend keyword only in the prototype found in the class declaration. You don’t use it in the function definition unless the definition is also the prototype.
Listing 11.10. mytime3.h
// mytime3.h -- Time class with friends
#ifndef MYTIME3_H_
#define MYTIME3_H_
#include
class Time
{
private:
int hours;
int minutes;
public:
Time();
Time(int h, int m = 0);
void AddMin(int m);
void AddHr(int h);
void Reset(int h = 0, int m = 0);
Time operator+(const Time & t) const;
Time operator-(const Time & t) const;
Time operator*(double n) const;
friend Time operator*(double m, const Time & t)
{ return t * m; } // inline definition
friend std::ostream & operator<<(std::ostream & os, const Time & t);
};
#endif
Listing 11.11 shows the revised set of definitions. Note again that the methods use the Time:: qualifier, whereas the friend function does not. Also note that because mytime3.h includes iostream and provides the using declaration std::ostream, including mytime3.h in mytime3.cpp provides support for using ostream in the implementation file.
Listing 11.11. mytime3.cpp
// mytime3.cpp -- implementing Time methods
#include "mytime3.h"
Time::Time()
{
hours = minutes = 0;
}
Time::Time(int h, int m )
{
hours = h;
minutes = m;
}
void Time::AddMin(int m)
{
minutes += m;