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>>
.
Remember
operator<< and operator>> use two <iostream> classes: istream and ostream. So, when a class overloads either operator, we must also add
#include <iostream> using namespace std;to the header file before the class specification.
Let's take an incremental approach transitioning from "regular" functions to operators to help us understand how operator<<
and operator>>
work. First, we'll explore two functions overloaded on a single parameter followed by functions overloaded on two parameters.
print(int x) { . . . } |
print(double x) { . . . } |
(a) | (b) |
print(ostream& out, int x) { . . . } |
print(ostream& out, double x) { . . . } |
(c) | (d) |
print(3);
- The value 3 is processed as an integer, which matches the first version of the function.print(3.14);
- 3.14 is a double and so matches the second function.print(cout, 5);
- these parameters match the third function. cout
, an instance of a class named ostream
, sends output to the console.print(cout, 5.7);
- these parameters match the fourth function.Examples (c) and (d) are are similar to how operator<<
is implemented in the ostream
class included with the iostream
header file. Other classes may also overload the inserter operator and prototype the function in their header file.
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) |
(e.i) | (e.ii) |
<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<iostream>
ostream
attaches to an expression in the program; the "sprayer" or destination end attaches to a console windowistream
attaches to the keyboard; the "sprayer" or destination end attaches to a variable in the program