11.5.1. Overloading operator<< and operator>>

Time: 00:03:45 | Download: Large, Large (CC), small | Streaming Streaming (CC) | Slides (PDF)

Programmers frequently overload the inserter, operator<<, and the extractor, operator>>, because they are the main output and input functions for C++ programs. Each function follows a fixed pattern. While the pattern is rigid and unchanging, some elements are flexible and left to programmers to name. Specifically, programmers may choose the parameter names, and the class for which the operator is overloaded determines the type name of the second parameter. Everything else in the pattern is fixed and required. A simplified and highly abridged version of the fraction class demonstrates the patterns and the programmer-named elements.

friend ostream& operator<<(ostream& out, fraction & f)
{
	// format and print f's members
	return out;
}
friend istream& operator>>(istream& in, fraction & f)
{
	// read data into f's members
	return in;
}
Patterns in operator<< and operator>>. Categorizing the I/O operators' elements has two benefits. The categories make it easier to describe their purpose and provide structure, prolonging learner retention. The categories also form a convenient checklist programmers can follow during implementation.
  • Unchanging elements:
    • The functions are implemented as friends
    • operator<<
      • Returns an ostream reference
      • The first parameter is an ostream reference
    • operator>>
      • Returns an istream reference
      • The first parameter is an istream reference
    • The functions return the first parameter
  • The class befriending the function:
    • Sometimes works as a non-reference
    • Always works as a reference
  • Programmer-chosen names
    • The stream parameter names
    • The object target of the I/O operation
  • Function-specific tasks
    • Format and send member variables to the output stream
    • Read values from the input stream and save them in the member variables

operator<< And operator>> Examples

class fraction
{
    private:
        int numerator;
        int denominator;

    public:
				.
				.
				.
        friend ostream& operator<<(ostream& out, fraction& f);
        friend istream& operator>>(istream& in, fraction& f);
				.
				.
    private:
	void reduce();
};
ostream& operator<<(ostream& out, fraction& f)
{
    out << f.denominator << "/" << f.numerator;

    return out;
}

istream& operator>>(istream& in, fraction& f)
{
    cout << "Please enter the numerator: ";
    in >> f.numerator;
    cout << "Please enter the denominator: ";
    in >> f.denominator;
    reduce();

    return in;
}
(a)(b)
fraction left;
fraction right;

cin >> left;
cin >> right;
fraction result = left + right;
cout << result << endl;
A picture showing two mappings. First, 'cin >> left' and 'cin >> left' map to 'operator>>(in,f)'. Second, 'cout << result' maps to 'operator<<(out,f)'. The mappings corresponds to the code illustrated in (b) and (c).
(c)(d)
Abridged version of the fraction class. Programmers can define or prototype the I/O functions in the class specification, depending on their size. The friend keyword is always a part of the function declaration, not the definition.
  1. The fractionclass has two private member variables or fields: numerator and denominator. It also has prototypes for the inserter and extractor operators and a private helper helper function, reduce that reduces a fraction to lowest terms. Programmers put the class specification in a header file and function definitions in a source code file. The prototypes retain the friend keyword.
  2. The definitions of the inserter and extractor functions. Programmers define longer functions in source code files where the definition does not include "friend." After inputting values into the numerator and denominator, the extractor reduces the fraction to lowest terms by calling reduce.
  3. Client code defining two fraction objects and illustrating three overloaded operators: operator<<, operator>>, and operator+.
  4. Overloaded operators are called using an operator syntax. The client code (c) calls the extractor, operator>>, twice to fill the fraction objects forming the addition operator's left and right operands. The client calls the inserter, operator<<, to display the addition operation's result.
The constructor in the first fraction example reduces new fraction objects to lowest terms. Moving the reduction code from the constructor to a helper function allows programmers to use it in the constructor and inserter function without duplication, demonstrating a common way to use helper functions. The fraction 2 example (later in the chapter) demonstrates the reduce function.
Prompts In operator>>

The operator>> illustrated in (b) includes "Please enter . . ." prompts. The prompts make sense when a person responds by entering input through the console. However, an istream can represent many input sources. The prompts are inappropriate when the input is automatic, for example, when a program reads input from a file or a device that doesn't require human interaction. In these cases, the prompt is unnecessary and potentially confusing. Furthermore, the prompts can negatively impact the program's performance.

Writing output to the console is a relatively slow process. The time needed to write a prompt is insignificant compared to the time needed for a person to enter the requested input. But, imagine the overloaded operator>> running in a loop. The numerous prompts "clutter" the screen and take a significant portion of the time needed to complete the input operation, slowing down the program. Generalizing the extractor is a better approach. The client program "knows" the data source and whether or not to prompt for input. Putting the prompts in the client code makes the extractor usable in diverse situations.