Program2-3 continued

advertisement
116095596
-1-
CHAPTER 2
INTRODUCTION TO :
Phases of the software life cycle
Differences between structured approach and OOP
Runtime error
Exception
Constructor Design
Operator Overloading
116095596
-2-
Object-Oriented Program Design
Software Development Methodology
Problem Analysis/Program Definition
- The programmer analyzes the problem with the client
- Determines what form the input and output should take
- Determines algorithms
Design Phase
- Object oriented design differs greatly from the traditional structured design
- Traditional structured design is modular and hierarchical – top down.
- Object oriented designs use Objects as the primary building blocks of the program
- Control modules direct the interaction among the objects
- The programmer writes a declaration for each class
- Class methods are tested INDIVIDUALLY outside the realm of
the large application, under controlled conditions ( major advantage )
- Top-down design consists of a main program and subprograms to control
the interaction of the objects
- The main program and subprograms from the design framework.
Coding ( Implementation Phase)
- The main program and subprograms that implement the program design framework
are written in the coding phase
Testing Phase
We can verify a program’s coding by testing each object’s interaction with the control
modules in the design framework.
Maintenance Phase
This phase begins as soon as the client installs the system and software systems are
regularly upgraded
116095596
-3-
Handling Runtime Errors:
- Terminate Program – ex: call exit() function
- Set a Flag
Setting a flag in object oriented programming has its problems.
For example: If the error occurs in a method contained in the object – what
should the object do? Handle the error? Return a return value to the client?
Use e reference argument?
-C++ Exceptions
Are the most flexible for of error reporting. An exception is an object that contains
information that is transmitted without using the normal function return process.
The function that encounters the error instantiates an object and the exception is reported
back to the block of code that is designed to catch the exception. This block of code is
called the exception handler.
See hand out for details and examples.
116095596
-4-
Object Composition
The term composition refers to a condition that exists when a class (client class) contains
one or more data members that are objects of class (supplier class) type.
#ifndef TIMECARD_CLASS
#define TIMECARD_CLASS
#include <iostream>
#include "d_time24.h"
#include "d_except.h"
#include "d_util.h"
using namespace std;
class timeCard
{
public:
timeCard(const string& ssno, double rate, int punchInHour,
int punchInMinute);
void punchOut(const time24& t);
// assign t to punchOutTime and set hasPunched to true
void writeSalaryInfo();
// output a log that includes the beginning and ending times for
// the day's work, the amount of time worked, the pay rate
// and the earnings.
// precondition: throw a rangeError exception if worker has not
//
punched out (hasPunched == false)
private:
string workerID;
time24 punchInTime, punchOutTime;
double payrate;
bool hasPunched;
};
// supplier-class objects
116095596
-5-
Constructor with Composition:
// use initialization list to initialize data members
timeCard::timeCard ( const string& ssno, double rate, int punchInHour, int punchInMinute):
workerID(ssno), payrate(rate), hasPunched(false),
punchInTime(punchInHour, punchInMinute)
{ }
// alternative implementation
// this technique works only because time24 have default constructors
// in general use initialization list
timeCard::timeCard(const string& ssno, double rate, int punchInHour, int punchInMinute):
{
workerID = ssno;
payrate = rate;
hasPunched = false;
punchInTime = time24(punchInHour, punchInMinute);
}
void timeCard::punchOut(const time24& t)
{
punchOutTime = t;
hasPunched = true;
}
void timeCard::writeSalaryInfo()
{
// throw an exception if the worker did not punch out
if (hasPunched == false)
throw rangeError("timeCard: writeSalaryInfo() called before "
"punching out");
// total time worked
time24 timeWorked = punchInTime.duration(punchOutTime);
// hours and fraction of an hour worked
double hoursWorked = timeWorked.getHour() + timeWorked.getMinute()/60.0;
// format the output
cout << "Worker:
" << workerID << endl;
cout << "Start time: ";
punchInTime.writeTime();
cout << " End time: ";
punchOutTime.writeTime();
cout << endl;
cout << "Total time: " << setreal(1,2) << hoursWorked
<< " hours" << endl;
cout << "At $" << setreal(1,2) << payrate
<< " per hour, the days earnings are $"
<< setreal(1,2) << payrate*hoursWorked << endl;
}
#endif
// TIMECARD_CLASS
116095596
-6-
Program example with composition and exception handler:
//
//
//
//
//
//
//
//
File: prg2_2.cpp
the program simulates a temporary employees arriving at work and leaving when the
plant closes at 5:00 PM.it prompts for the hour and minute of arrival at work
and the social security number. it uses the timeCard class to determine the
employee pay for the day. The program uses a random number to simulate that 3 out
of every 4 employees punch out. in this situation, the program must handle a
rangeError exception thrown by the timeCard class when an employee does not punch
out. The catch block simulates the supervisor punching out for the employee
#include <iostream>
#include "d_tcard.h"
#include "d_random.h"
// use timeCard class
// randomNumber class
using namespace std;
int main()
{
const double PAYRATE = 12.50; // posted pay rate
const time24 CHECKOUT(17,0); // work ends at 5:00 PM
string id; // employee data input from the keyboard
int inHour, inMinute;
randomNumber rnd; // simulate 1/4 of employees forgetting to punch out
cout << "Enter hour and minute of arrival at work: ";
cin >> inHour >> inMinute;
cout << "Enter social security number: ";
cin >> id;
// declare a timeCard object for the employee
timeCard employee(id, PAYRATE, inHour, inMinute);
// represents 3 out of 4 employees punching out
if (rnd.frandom() > .25)
employee.punchOut(CHECKOUT); // punch out
// include writeSalary() call in a try block. it
// throws the rangeError exception if the employee
// has not punched out
try
{
employee.writeSalaryInfo();
}
catch (const rangeError& re)
{
// output the error string in the rangeError object re
cerr << re.what() << endl;
// supervisor punches out the employee. display the salary information
employee.punchOut(CHECKOUT);
employee.writeSalaryInfo();
}
return 0;
}
116095596
-7-
Program example Output:
/*
Run 1:
Enter hour and minute of arrival at work: 8 00
Enter social security number: 345-27-8156
Worker:
345-27-8156
Start time: 8:00
End time: 17:00
Total time: 9.0 hours
At $12.50 per hour, the days earnings are $112.50
Run 2:
Enter hour and minute of arrival at work: 9 15
Enter social security number: 766-25-6728
timeCard: writeSalaryInfo() called before punching out
Worker:
766-25-6728
Start time: 9:15
End time: 17:00
Total time: 7.75 hours
At $12.50 per hour, the days earnings are $96.88
116095596
-8-
Operator Overloading
Operator Overloading- i.e. redefining standard operator symbols ( e.g. + , [ ] , ... ), is a
feature that allows us to implement operations for an abstract data type using standard
language operators (with their precedence and associativity properties) as class methods.
Standard Class Method
Overloaded Operators
R = P. AddMat ( Q );
S = R . MultMat ( Q.AddMat ( P ) );
R = P + Q;
S = R * ( Q + P );
Date ( 6, 6, 44 ) < ( Date ( 12, 25, 80 );
// compare two date objects
PrintDate Method:
Overloaded “<<” operator
cout << “ The last day ...... “;
D. PrintDate();
cout << “ the last day .. “ << D;
C++ allows overloading most of its native operators, including the stream operators. New
operators cannot be created. The term operator overloading implies that language operators
such as +, !=, [ ] , and = can be redefined for a class type. C++ provides a variety of
techniques for redefining operators.
C++ requires that at least one argument of an overloaded operator must be an object or
an object reference.
Techniques include:
Client-defined external ( free) functions, class members, and friends.
116095596
-9-
Operator Overloading (continued )
Client defined external (free ) function:
// Note: == operator tied to data type not class
int operator== ( const DataType& a, const DataType& b );
struct Employee
{
...
int ID;
}
int operator== (const Employee& a, const Employee& b )
{
return a.ID == b.ID;
// compare ID fields
}
int SeqList : : Find ( DataType& item ) const
{
int i = 0;
if ( ListEmpty() )
return 0;
// the relational equal operator compares list items
while ( i < size && ! ( item == listitem[i] ) )
i ++;
if ( i < size )
{
item = listitem[i];
return 1;
}
else
return 0;
}
116095596
- 10 -
Operator Overloading ( continued )
Class Members:
A class may have methods that combine objects in an expression using infix notation.
Example Vector Addition, Multiplication and Negation:
class Vec2d
{
private:
double x1, x2;
// private data members
public:
Vec2d ( double h = 0.0, double v = 0.0 ); // constructor default
// values
// member functions
Vec2d operator- (void);
// negation
Vec2d operator+ (const Vec2d& V );
// addition
double operator* ( const Vec2d& V );
// dot product
// friend functions
friend Vec2d operator* ( const double c, const Vec2d& V );
friend ostream& operator<< ( ostream& os, const vec2d& U );
}
for instance, with the objects
Vec2d U ( 1,2 ), V ( 2,3 );
U + V = ( 1,2 ) + ( 2,3 ) = ( 3,5 )
-U = -(1,2) = (-1, -2 )
U * V = (1,2) * ( 2,3 ) = 8
116095596
- 11 -
Operator overloading requires:
1. Operators must obey the precedence, associativity, and number of operands dictated by
the built-in definition of the operator.
For example,
‘ * ‘ is a binary operator and must always have two
parameters when it is overloaded.
2. All operators in C++ can be overloaded, with the exception of the
following:
, ( comma operator )
sizeof
: : ( class scope operator )
? : ( conditional expression )
3. Overloaded operators cannot have default arguments; all operands for the
operator must be present.
4. When an operator is overloaded as a member function, the object
associated with the operator is always the left-most operand.
5. As member functions, the unary operators take no arguments, and binary
operators take one argument.
116095596
- 12 -
FRIEND FUNCTIONS:
Typically, only member functions of a class have access to the private data of a class.
Sometimes it may be necessary for non-members to have access to private data. This can be
accomplished by using friend functions. Friend functions are defined outside of the class
but can have access to private data members. A friend function is declared by preceding the
function declaration in the class with the keyword friend.
Example: Scalar multiplication ( each component of a vector is multiplied by a numeric
value.)
friend Vec2d operator* ( const double c, const vec2d& U );
The definition of the function is outside the scope of the class and is implemented as a
standard function. Both arguments are passed to ‘*’ and scalar is left hand operand.
Vec2d operator* ( double c, Vec2d U )
{
return Vec2d ( c * U.x , c * U.y ); // accesses private members of object
}
Rules :
1. Place the function declaration inside the class preceded by the keyword
friend . The function is defined outside the class and is not a member of
the class.
2. They keyword friend is used only within the function declaration in the
class and not with the function definition.
3. A friend function has access to private data members.
4. A friend function gains access to members of the class only by being
passed objects as parameters and using the dot notation to reference a
member.
5. In order to overload a unary operator as a friend, pass the operand as a
parameter. When overloading binary operators, pass both operands as
parameters.
116095596
- 13 -
Overloading Stream Operators
The file <iostream.h> contains declarations for two classes named ostream and istream. The
I/O stream systems provides the definitions for operators “>>” and “ <<”.
The user can overload the stream operators to implement I/O of a user-defined type.
Ex:
Date D;
cin >> D;
cout << D;
In would not be practical to explicitly declare the overloaded operators in <iostream.h> .
Overloading the stream operators uses friend functions .
class CL
{
...
public:
...
friend istream& operator>> ( istream& istr, CL& Variable );
friend ostream& operator<< ( ostream& ostr, const CL& Value )
};
116095596
- 14 -
Implementation: - Overloading stream input operator
// - reading a rational number ( ex: 3/4 )
istream& operator >> ( istream& istr, Rational& x )
{
char c;
istr >> x.num >> c >> x.den; // reads 3/4
if ( x.den = = 0 )
{
cerr << “ A zero denominator is invalid\n”;
exit (1);
}
x.Standardize ( ) ;
return istr ;
}
Note: - The Data item Variable “x” is passed by reference since it is
assigned a value.
- The parameter “istr” represents an input stream such as cin and since
the I/O process alters the state of the stream it must be passed by
reference
- The function returns a reference to an istream so that the operator can
be used in a chain.
cin >> m >> n ;
Implementation: Overloading stream output operator
ostream& operator << ( ostream& ostr, const Rational& x )
{
ostr << x.num << ‘/’ << x.den; // access private data members
// because overloaded stream operator
// is a friend function of the class
return ostr;
}
116095596
- 15 -
Conversion from Object Type
C++ language defines explicit conversions ( ex: cast ) or implicit conversion (ex: assigning
double to a long )between primitive types.
A class may contain member function(s) that convert an object to a value of another data
type.
Ex:
class CL
{
........
operator NewType ( void );
};
NewType a; // Instantiate object of type NewType
CL obj;
// Instantiate object of type CL
a = NewType(obj); // Explicit conversion
a = obj;
// Implicit conversion
The operator NewType() takes an object and returns a value of type NewType. The target
type NewType, is often a standard type such as int or float.
the operator NewType is a unary operator and therefore does not contain a parameter. There
is now return type - it is implicit in the name NewType.
116095596
- 16 -
Complete program 2-3 – illustrates operator overloading
#ifndef TIME24_CLASS
#define TIME24_CLASS
#include <iostream>
#include <iomanip>
#include "d_except.h"
// for rangeError exception
using namespace std;
#ifdef _MSC_VER
class time24;
time24 operator+ (const time24& lhs, const time24& rhs);
time24 operator+ (const time24& lhs, int min);
time24 operator+ (int min, const time24& rhs);
time24 operator- (const time24& lhs, const time24& rhs);
bool operator== (const time24& lhs, const time24& rhs);
bool operator< (const time24& lhs, const time24& rhs);
istream& operator>> (istream& istr, time24 &r);
ostream& operator<< (ostream& ostr, const time24& r);
#endif
// _MSC_VER
class time24
{
public:
time24(int h = 0, int m = 0);
// constructor initializes hour and minute
void addTime(int m);
// update time by adding m minutes to the current time
// Precondition: m must be >= 0
// Postcondition: The new time is m minutes later
time24 duration(const time24& t);
// return the length of time from the current time to some later
// time t as a time24 value
// Precondition: time t must not be earlier than the current time.
// if it is, throw a rangeError exception
void readTime();
// input from the keyboard time in the form hh:mm
// Postcondition: Assign value hh to hour and mm to minute and
// adjust units to the proper range.
void writeTime() const;
// display on the screen the current time in the form hh:mm
int getHour() const;
// return the hour value for the current time
int getMinute() const;
// return the minute value for the current time
116095596
Program 2-3 ( continued )
- 17 -
// THESE FUNCTIONS ARE DISCUSSED IN CHAPTER 2 ON OPERATOR
// OVERLOADING
friend bool operator== (const time24& lhs, const time24& rhs);
friend bool operator< (const time24& lhs, const time24& rhs);
friend time24 operator+ (const time24& lhs, const time24& rhs);
// form and return lhs + rhs
friend time24 operator+ (const time24& lhs, int min);
// form and return lhs + min
// Precondition: min must be >= 0
friend time24 operator+ (int min, const time24& rhs);
// form and return min + rhs
// Precondition: min must be >= 0
friend time24 operator- (const time24& lhs, const time24& rhs);
// form and return lhs - rhs
// Precondition: lhs >= rhs. if not, throw a rangeError exception
time24& operator+= (const time24& rhs);
// current object = current object + rhs
// Postcondition: the time increases by the value of rhs
time24& operator+= (int min);
// current object = current object + min
// Precondition: min must be >= 0
// Postcondition: the time increases by min minutes
friend istream& operator>> (istream& istr, time24& t);
// input t in the format hh:mm. may omit the leading digit
// if hours or minutes < 10
friend ostream& operator<< (ostream& ostr, const time24& t);
// output t in the format hh:mm. always include two digits
// for the minute (e.g. 15:07). hours before 12 use 1 digit
// and precede the hour by a blank (e.g. " 7:15")
private:
int hour, minute;
// data members
// utility function sets the hour value in the range 0 to 23
// and the minute value in the range 0 to 50
void normalizeTime();
};
116095596
Program 2-3 ( continued)
- 18 -
// ***********************************************************
//
time24 class implementation
// ***********************************************************
// set minute and hour within their proper ranges
void time24::normalizeTime()
{
int extraHours = minute / 60;
// set minute in range 0 to 59
minute %= 60;
// update hour. set in range 0 to 23
hour = (hour + extraHours) % 24;
}
// constructor. initialize time data
time24::time24(int h, int m) : hour(h), minute(m)
{
// put hour and minute in correct range
normalizeTime();
}
// add m minutes to the time
void time24::addTime(int m)
{
// add m to minute. minute may exceed 59, so normalize
minute += m;
normalizeTime();
}
time24 time24::duration(const time24& t)
{
// convert current time and time t to minutes
int currTime = hour * 60 + minute;
int tTime = t.hour * 60 + t.minute;
// if t is earlier than the current time, throw an exception
if (tTime < currTime)
throw rangeError(
"time24 duration(): argument is an earlier time");
else
// create an anonymous object as the return value
return time24(0, tTime-currTime);
}
void time24::readTime()
{
char colonSeparator;
cin >> hour >> colonSeparator >> minute;
// make sure hour and minute are in range
normalizeTime();
}
116095596
Program 2-3 continued
- 19 -
// output time in the format <hour>:<minute>
void time24::writeTime() const
{
// the implementation uses stream handling functions
// not discussed in the book. consult your compiler
// help system for details
// save current format flags and fill character
long currentFlags = cout.flags();
char currentFill = cout.fill();
// set fill char to ' ' and enable right justification
cout.fill(' ');
cout.setf(ios::right,ios::adjustfield);
// output the hour
cout << setw(2) << hour << ':';
// set fill char to '0' and output the minute
cout.fill('0');
cout << setw(2) << minute << " ";
// restore the fill char and the format flags
cout.fill(currentFill);
cout.setf(currentFlags);
}
int time24::getHour() const
{
return hour;
}
int time24::getMinute() const
{
return minute;
}
// compare hours and minutes
bool operator== (const time24& lhs, const time24& rhs)
{
return lhs.hour == rhs.hour && lhs.minute == rhs.minute;
}
// convert the hour and minute values for each operand to
// minutes. compare lhs in minutes with rhs in minutes
bool operator< (const time24& lhs, const time24& rhs)
{
return (lhs.hour*60 + lhs.minute) < (rhs.hour*60 + rhs.minute);
}
// create an anonymous object with hour = lhs.hour + rhs.hour
// and minute = lhs.minute+rhs.minute.
time24 operator+ (const time24& lhs, const time24& rhs)
{
return time24(lhs.hour+rhs.hour, lhs.minute+rhs.minute);
}
116095596
Program2-3 continued
- 20 -
// create an anonymous object with hour = lhs.hour and
// minute = lhs.minute + min.
time24 operator+ (const time24& lhs, int min){
return time24(lhs.hour, lhs.minute + min);
}
// return the value rhs + min that is computed by
// time24 operator+ (const time24& lhs, int min)
time24 operator+ (int min, const time24& rhs)
{
return rhs + min;
}
// using the < operator, check whether lhs < rhs is true. if so
// terminate; otherwise return a time24 result built
// using the constructor
time24 operator- (const time24& lhs, const time24& rhs)
{
if (lhs < rhs)
throw rangeError("time24 operator-: Cannot subtract later from earlier time");
// convert each object to minutes and compute the
// difference in minutes. return the time24 object
// having 0 as hours and the value of the difference
// as minutes
return time24(0, (lhs.hour*60 + lhs.minute) (rhs.hour*60 + rhs.minute));
}
// implement += by using addition with operands *this and rhs
time24& time24::operator+= (const time24& rhs)
{
// add *this and rhs using overloaded + operator
*this = *this + rhs;
// return a reference to the current object
return *this;
}
// implement += by using addition with operands *this and min
time24& time24::operator+= (int min)
{
// add *this and min using overloaded + operator
*this = *this + min;
// return a reference to the current object
return *this;
}
116095596
Program 2-3 continued
- 21 -
// overload stream operator >>. input has the form
// hour:minute
istream& operator>> (istream& istr, time24& t)
{
char separatorChar;
istr >> t.hour >> separatorChar >> t.minute;
// make sure hour and minute are in range
t.normalizeTime();
// return the stream
return istr;
}
// overload stream operator <<. output in the form
// hour:minute
ostream& operator<< (ostream& ostr, const time24& t)
{
// the implementation uses stream handling functions
// not discussed in the book. consult your compiler
// help system for details
// save current format flags and fill character
long currentFlags = ostr.flags();
char currentFill = ostr.fill();
// set fill char to ' ' and enable right justification
ostr.fill(' ');
ostr.setf(ios::right,ios::adjustfield);
// output the hour
ostr << setw(2) << t.hour << ':';
// set fill char to '0' and output the minute
ostr.fill('0');
ostr << setw(2) << t.minute;
// restore the fill char and the format flags
ostr.fill(currentFill);
ostr.setf(currentFlags);
return ostr;
}
#endif // TIME24_CLASS
116095596
- 22 // File: prg2_3.cpp
// prompt the user for the time a movie starts and the
// length of the movie. use the time24 + operator to determine
// when the movie ends. a bus always arrives in front of
// the theater at 22:45. use the time24 - operator to compute
// the waiting time at the bus stop. output the time the movie
// ends and the waiting time
#include <iostream>
#include "d_time24.h"
using namespace std;
int main()
{
time24 startMovie, movieLength, endMovie,
busArrival(22,45), waitingTime;
// prompt and input times for movie to start and its length
cout << "Enter the time the movie starts and its length: ";
cin >> startMovie >> movieLength;
// compute the time the movie ends and the waiting time for the bus
endMovie = startMovie + movieLength;
waitingTime = busArrival - endMovie;
cout << "Movie ends at " << endMovie << endl;
cout << "Waiting time for the bus is " << waitingTime << endl;
return 0;
}
/*
Run:
Enter the time the movie starts and its length: 20:15 1:50
Movie ends at 22:05
Waiting time for the bus is 0:40
*/
Download