<<
)Rather than writing a print()
member function for a class, it can be convenient to overload the stream insertion operator to work with our new data type.
Because the left operand of the stream insertion operator is an object of class ostream
(like cout
) and not an object of our new class, we must overload the operator as a standalone function.
Here is the skeleton code for overloading the stream insertion operator:
ostream& operator<<(ostream& lhs, const ClassName& rhs) { // Print the data members of rhs using lhs like you would using cout // Return the output stream object so the operator may be cascaded correctly return lhs; }
Example 1: The <<
operator overloaded as a standalone friend
function of the Rational
class
To designate our overloaded operator function as a friend
of the Rational
class, we need to include the function's prototype, preceded by the keyword friend
, anywhere in the declaration of the Rational
class:
class Rational
{
friend ostream& operator<<(ostream&, const Rational&);
private:
int numerator,
denominator;
public:
.
.
.
};
The function definition can then be coded like so:
ostream& operator<<(ostream& lhs, const Rational& rhs)
{
lhs << rhs.numerator << "/" << rhs.denominator;
return lhs;
}
Once we've overloaded this operator, we can use it to print an object of the Rational
class using cout
instead of calling a print()
member function:
Rational r1(3, 5); // Create r1 and set it = 3/5
cout << r1; // Generates the function call operator<<(cout, r1) and prints 3/5
The return value from the overloaded operator function is required in order to cascade the operator correctly. Consider the following typical use of the cascaded stream insertion operator. How does the compiler evaluate this statement?
cout << "The value of r1 is " << r1 << endl;
The <<
operator is evaluated from left to right, so this part of the expression is evaluated first:
cout << "The value of r1 is "
The text string is printed and then a reference to the ostream
object cout
is returned. The return value effectively replaces the sub-expression that has been evaluated, and as a result, the remainder of the expression becomes:
cout << r1 << endl;
The overloaded stream insertion operator function then generates a function call to print r1
and (if written correctly) returns a reference to the ostream
object that was passed into the function (cout
, in this case). The return value replaces the function call and the remaining part of the expression then becomes:
cout << endl;
If your overloaded stream insertion operator function doesn't return a reference to the ostream
object passed in as the first argument, the last step would not work correctly - you'd get a syntax error on the statement instead since the left operand for the next call to the operator function would be the wrong data type.
Writing an overloaded operator function to use the <<
operator to write to a file stream is trivial - simply replace the data type ostream
with the data type ofstream
in the function prototype and definition. Of course, you'll have to open the file stream object for output before you can write to it.