11.5. operator<< And operator>>

Time: 00:02:36 | Download: Large, Large (CC), Small | Streaming, Streaming (CC) | Slides (PDF)
Review

Creating and using the inserter and extractor functions builds on concepts introduced in previous chapters. Please review the following as needed:

Overloaded operators are "overloaded" because they define a new meaning for an operator to which the language has already assigned a meaning. Like overloaded functions, we can overload an operator many times, but we must observe two requirements. First, we may only overload an operator in the context of a new class, and second, at least one operand must be unique to distinguish the operator from the others. While these requirements apply to all overloaded operators, understanding them is especially important for using operator<< and operator>>.

Header File Requirement

The inserter and extractor operators, operator<< and operator>>, use two <iostream> classes, istream and ostream, as parameter types. Therefore, the class names appear in the functions' signatures in the class specification. When programmers overload either operator, they must include the <iostream> header before the class specification:
#include <iostream>
using namespace std;

Revisiting Arguments, Parameters, and Overloaded Functions

We take an incremental approach, transitioning from "regular" functions to operators, to help us understand how operator<< and operator>> work. First, we explore two functions overloaded on a single parameter, followed by functions overloaded on two parameters.

void print(int x);

print(3);
void print(double x);

print(3.14);
void print(ostream& out, int x);

print(cout, 5);
void print(ostream& out, double x);

print(cout, 5.7);
(a)(b)(c)(d)
Overloaded functions. Each group presents a function prototype and a corresponding function call. When a program has overloaded functions, it determines to which function calls refer by matching the data type of the arguments in the function call to the parameters in the function prototype. If the overloaded functions have multiple arguments, one unique argument is sufficient to distinguish between any number of functions with the same name.
  1. The program processes the value 3 as an integer, matching the first version of the function.
  2. The value 3.14 is a double, matching the second function.
  3. The program "looks at" the second argument, matching the third function.
  4. The program selects the fourth function based on the second argument.
cout is an instance of ostream. Although the (c) and (d) functions have the same first argument, the compiler can distinguish them by their second. The following figure illustrates the many overloaded versions of the inserter and extractor operators already provided by the ostream and istream classes. However, these overloaded functions do not prevent programmers from overloading them for their classes.

The C++ I/O Operators

class ostream
{
    public:
        friend ostream & operator<<(ostream &, char);
        friend ostream & operator<<(ostream &, char *);
        friend ostream & operator<<(ostream &, short);
        friend ostream & operator<<(ostream &, int);
        friend ostream & operator<<(ostream &, long);
        friend ostream & operator<<(ostream &, float);
        friend ostream & operator<<(ostream &, double);
};
class istream
{
    public:
        friend istream & operator>>(istream &, char &);
        friend istream & operator>>(istream &, char *);
        friend istream & operator>>(istream &, short &);
        friend istream & operator>>(istream &, int &);
        friend istream & operator>>(istream &, long &);
        friend istream & operator>>(istream &, float &);
        friend istream & operator>>(istream &, double &);
};
(a)(b)
class string
{
  public:
     friend ostream& operator<<(ostream& out, string& s);
     friend istream& operator>>(istream& in, string& s);
};
class fraction
{
  public:
     friend ostream& operator<<(ostream& out, fraction& f);
     friend istream& operator>>(istream& in, fraction& f);
};
(c)(d)
A picture illustrating a program sending output to the console through an 'ostream' object. The output is the result of evaluating any valid expression. A picture illustrating a program reading data from the console into a variable through an 'istream object.'
(e.i)(e.ii)
The inserter and extractor operator prototypes in <iostram>.
  1. Some of the overloaded inserter operators prototyped in <iostream> (some detail is elided for simplicity). With the exception of C-strings, all primitive data type arguments are passed by value. The function stores or returns the value read from the input stream into the second argument, which requires that the argument be passed by reference
  2. Some of the overloaded extractor operators prototyped in <iostream>
  3. Standard or library classes often overload the inserter and extractor operators, as illustrated by the string class. Objects are typically passed by reference
  4. Application programmers can also overload the inserter and extractor operators when appropriate. Overloaded versions of the inserter and extractor operators for the fraction class are illustrated here
  5. The stream objects are like a garden hose that carries a stream of bytes:
    1. The "faucet" or source end of an ostream attaches to an expression in the program; the "sprayer" or destination end attaches to a console window
    2. The "faucet" or source end of an istream attaches to the keyboard; the "sprayer" or destination end attaches to a variable in the program