11.8.1. Time Example (Overloaded Operators Version)

Time: 00:13:47 | Download: Large Small | Streaming |

 

Review

The Time demonstration program relies on concepts introduced in earlies chapters. Please review the following as needed:

The overloaded operator version of the Time example begins with the class version of Time and modifies it to use overloaded operators for input, output, and addition. It still consists of three files, and each file requires some 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 complete the addition operation.

operator+ As a Member Function

UML class diagram for the Time class:
Time
----------------------
-hours : int
-minutes : int
-seconds : int
----------------------
+Time()
+Time(h : int, m : int, s : int)
+Time(s : int)
+operator+(t2 : Time) : Time
+operator+(i : int) : Time
+operator<<(out : ostream&, t : Time&) : ostream&
+operator>>(in : istream&, t : Time&) : istream&
The Time UML class diagram with overloaded operators.
  1. The constructors remain unchanged from previous (class) version
  2. The add function is replaced by two overloaded operator+ functions, both of which are members of the Time class
  3. The print function is replaced by operator<<
  4. The read function is replaced by operator>>
  5. The UML does not have a standard notation for friend functions but operator<< and operator>> are always implemented as "friends"

The UML class diagram is converted to a C++ class as follows:

#include <iostream>
using namespace std;

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);

	Time	operator+(Time t2);
	Time	operator+(int i);

	friend	ostream& operator<<(ostream& out, Time& t);
	friend	istream& operator>>(istream& in, Time& t);
};
Time.h:
#include "Time.h"
#include <iostream>
#include <iomanip>
using namespace std;


Time::Time(int s)
{
	hours = s / 3600;
	s %= 3600;		// s = s % 3600;
	minutes = s / 60;
	seconds = s % 60;
}

Time Time::operator+(Time t2)
{
	int	i1 = hours * 3600 + minutes * 60 + seconds;
	int	i2 = t2.hours * 3600 + t2.minutes * 60 + t2.seconds;

	return Time(i1 + i2);
}

ostream& operator<<(ostream& out, Time& t)
{
	out.fill('0');
	out << t.hours << ":" << setw(2) << t.minutes <<
		":" << setw(2) << t.seconds;
	out.fill(' ');

	return out;
}

istream& operator>>(istream& in, Time& t)
{
	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.cpp:

The read function introduced in chapter 5 and modified for chapter 6 could only read from the console. The read function did not have a parameter representing the source from which the function would read the data. Instead, the function hard-coded cout as the data source. So including the prompts in the read function itself was appropriate. The operator<< presented in Figure 4 retained the prompts to minimize the changes occurring to the function between chapters.

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 a program that runs unattended, reading input from a file. In that case, there is no one to read 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 in the driver appearing below illustrate this approach.

istream& operator>>(istream& in, Time& t)
{
	in >> t.hours;
	in >> t.minutes;
	in >> t.seconds;

	return in;
}
Alternate operator<< The function is more general (i.e., can be used in more situations) without the prompts.

A simple driver program stands in for a Time client and demonstrates calls to the Time 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;

	//cout << t + s << endl;	// alternate

	return 0;
}
driver.cpp:
Although overloaded operators may be called using a functional notation (e.g., operator>>(cin, t) or t.operator+(s) ), 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. Overloaded operators are called using an operator notation:
  • t + s
  • cin >> t
  • cout << s

operator+ As a friend Function

The two operator+ functions defined in the example above can support two related, but distinct function calls:

  1. Time + Time
  2. 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 write a version of operator+ as a member function that allows an int as the first argument (Figure 2). Fortunately, an appropriate operator+ function can be created 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 (Figure 3).

UML class diagram for the Time class:
Time
----------------------
-hours : int
-minutes : int
-seconds : int
----------------------
+Time()
+Time(h : int, m : int, s : int)
+Time(s : int)
+operator+(t1 : Time, t2 : Time) : Time
+operator<<(out : ostream&, t : Time&) : ostream&
+operator>>(in : istream&, t : Time&) : istream&
The Time UML class diagram with overloaded operators.
  1. The constructors remain unchanged from previous version (the third constructor is a conversion constructor - it converts an int to Time)
  2. The add function is replaced by a single overloaded operator+ implemented as a friend function
  3. Although the UML does not have a standard notation for denoting friend functions, it does support a kind of abbreviated comment or label called a stereotype. Stereo types are labels that appear between guillemets: « and ». A stereotype is used in this UML class diagram to denote that operator+ is implemented as a friend function
  4. operator<< and operator>> remain friend functions

The changes required to convert the Time class to use a friend function for operator+ are illustrated in the last two figures:

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);
};
Time.h: Two operator+ functions are replaced by a single function. Refering to Figure 2, we discard operator+(int i), and change operator+(Time t2) to operator+(Time t1, Time t2).
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 operator+. This version of the Time class modifies one function from the previous implementation (Figure 3 above), and highlights the important changes. Notice that the friend keyword only appears in the class specification, not the function definition.