Review
The overloaded operator version of the Time example continues the evolution of Time that began as a structure, was converted to a class with member functions, and now is given overloaded operators. It still consists of three files, each requiring modification to complete the conversion to overloaded operators. Two versions of the example follow: The first version uses two member functions to implement the addition operation. In contrast, the second version uses only a single friend (i.e., non-member) function to perform the addition operation.
friend
functionsThe following figure illustrates one way programmers can convert the UML class diagram to a C++ class.
#include <iostream> // (a) using namespace std; class Time { private: int hours = 0; // (b) int minutes = 0; int seconds = 0; public: Time() {} // (c) Time(int h, int m, int s) : hours(h), minutes(m), seconds(s) {} // (d) Time(int s); Time operator+(Time t2); // (e) Time operator+(int i); friend ostream& operator<<(ostream& out, Time& t); // (f) friend istream& operator>>(istream& in, Time& t); // (g) };
Time + Time
Time + int
int + Time
#include "Time.h" // (a) #include <iostream> #include <iomanip> using namespace std; Time::Time(int s) // (b) { hours = s / 3600; s %= 3600; // s = s % 3600; minutes = s / 60; seconds = s % 60; } Time Time::operator+(Time t2) // (c) { int i1 = hours * 3600 + minutes * 60 + seconds; int i2 = t2.hours * 3600 + t2.minutes * 60 + t2.seconds; return Time(i1 + i2); } Time Time::operator+(int i) // (d) { int i1 = hours * 3600 + minutes * 60 + seconds; return Time(i1 + i); } |
ostream& operator<<(ostream& out, Time& t) // (e) { out.fill('0'); out << t.hours << ":" << setw(2) << t.minutes << ":" << setw(2) << t.seconds; out.fill(' '); return out; } istream& operator>>(istream& in, Time& t) // (f) { cout << "Please enter the hours: "; cin >> t.hours; cout << "Please enter the minutes: "; cin >> t.minutes; cout << "Please enter the seconds: "; cin >> t.seconds; return in; } |
Time operator+(int i) { return Time(hours * 3600 + minutes * 60 + seconds + i); }
The read function introduced in chapter 5 and modified for chapter 6 could only read from the console. It did not have a parameter allowing it to read input from other locations, so it was appropriate to include the prompts in the function. The inserter presented above retained the prompts, minimizing the changes between versions. However, operator<< does include a parameter (the first one) denoting the data source. We can write a program that uses the operator interactively by asking an end-user to input data. However, we can also write an unattended program that reads input from a file. In that case, there is no one to see and respond to the prompt, and the prompt can slow the program. I recommend moving the prompts to the application code (the client that uses the Time class) and adapting them as necessary. The following version of operator>> and the driver below illustrate this approach.
istream& operator>>(istream& in, Time& t) { in >> t.hours; in >> t.minutes; in >> t.seconds; return in; }
A simple driver program demonstrating the Time class and its functions.
#include <iostream> #include "Time.h" using namespace std; int main() { Time t; cout << "Enter the hours, minutes, and seconds "; cout << "(press \"Enter\" after each entry):" << endl; cin >> t; cout << t << endl; Time s(1, 30, 4); cout << s << endl; Time u = t + s; cout << u << endl; return 0; }
operator>>(cin, t)
for "friends" or t.operator+(s)
for members, there is no reason for doing so - if we wanted to use functional notation, we would continue using function names like read, print, and add. In practice, programmers exclusively call overloaded operators with an operator notation:
t + s
cin >> t
cout << s
The two operator+ functions defined in the example above can support two similar but distinct function calls:
Time + Time
Time + int
Mathematically, int + Time
is also valid, but this function call does not match the argument list of either overloaded operator+ function. Furthermore, it is not possible to write a version of operator+ as a member function allowing an integer as the first argument (Non-member overloaded operators). Fortunately, we can write an appropriate operator+ function as a non-member friend
function. Furthermore, in conjunction with a conversion constructor, all three calling sequences may be satisfied with a single operator+ function (A single friend overloaded operator+).
friend
(non-member) operator+.
This version of the UML Time class diagram replaces the two operator+ member functions with a single friend
function. The "dog-eared" box is an optional UML feature allowing designers to annotate class diagrams. Its use here suggests that C++ programmers must implement the function as a friend
; otherwise, the only indication is the number of parameters.
class Time { private: int hours; int minutes; int seconds; public: Time() : hours(0), minutes(0), seconds(0) {} Time(int h, int m, int s) : hours(h), minutes(m), seconds(s) {} Time(int s); friend Time operator+(Time t1, Time t2); friend ostream& operator<<(ostream& out, Time& t); friend istream& operator>>(istream& in, Time& t); };
friend
operator+ replaces the two corresponding member functions. Note that the friend
keyword only appears in the class specification, not the function definition.
Time operator+(Time t1, Time t2) { int i1 = t1.hours * 3600 + t1.minutes * 60 + t1.seconds; int i2 = t2.hours * 3600 + t2.minutes * 60 + t2.seconds; return Time(i1 + i2); }
friend
version of the addition operator requires two explicit parameters because it is not bound to an object by the this
pointer. The figure shows it as it appears in a source code file (i.e., not in the class specification). The function's header does not include Time:: because it isn't a member function.