Case Study: A Date Class

Case Study A Date Class

The program of Figs. 11.1211.14 demonstrates a Date class. The class uses overloaded prefix and postfix increment operators to add 1 to the day in a Date object, while causing appropriate increments to the month and year if necessary. The Date header file (Fig. 11.12) specifies that Date's public interface includes an overloaded stream insertion operator (line 11), a default constructor (line 13), a setDate function (line 14), an overloaded prefix increment operator (line 15), an overloaded postfix increment operator (line 16), an overloaded += addition assignment operator (line 17), a function to test for leap years (line 18) and a function to determine whether a day is the last day of the month (line 19).

Figure 11.12. Date class definition with overloaded increment operators.

1 // Fig. 11.12: Date.h 2 // Date class definition. 3 #ifndef DATE_H 4 #define DATE_H 5 6 #include 7 using std::ostream; 8 9 class Date 10 { 11 friend ostream &operator<<( ostream &, const Date & ); 12 public: 13 Date( int m = 1, int d = 1, int y = 1900 ); // default constructor 14 void setDate( int, int, int ); // set month, day, year 15 Date &operator++(); // prefix increment operator 16 Date operator++( int ); // postfix increment operator 17 const Date &operator+=( int ); // add days, modify object 18 bool leapYear( int ) const; // is date in a leap year? 19 bool endOfMonth( int ) const; // is date at the end of month? 20 private: 21 int month; 22 int day; 23 int year; 24 25 static const int days[]; // array of days per month 26 void helpIncrement(); // utility function for incrementing date 27 }; // end class Date 28 29 #endif


Function main (Fig. 11.14) creates three Date objects (lines 1113)d1 is initialized by default to January 1, 1900; d2 is initialized to December 27, 1992; and d3 is initialized to an invalid date. The Date constructor (defined in Fig. 11.13, lines 1114) calls setDate to validate the month, day and year specified. An invalid month is set to 1, an invalid year is set to 1900 and an invalid day is set to 1.

Figure 11.13. Date class member- and friend-function definitions.

(This item is displayed on pages 610 - 611 in the print version)

1 // Fig. 11.13: Date.cpp 2 // Date class member-function definitions. 3 #include 4 #include "Date.h" 5 6 // initialize static member at file scope; one classwide copy 7 const int Date::days[] = 8 { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 9 10 // Date constructor 11 Date::Date( int m, int d, int y ) 12 { 13 setDate( m, d, y ); 14 } // end Date constructor 15 16 // set month, day and year 17 void Date::setDate( int mm, int dd, int yy ) 18 { 19 month = ( mm >= 1 && mm <= 12 ) ? mm : 1; 20 year = ( yy >= 1900 && yy <= 2100 ) ? yy : 1900; 21 22 // test for a leap year 23 if ( month == 2 && leapYear( year ) ) 24 day = ( dd >= 1 && dd <= 29 ) ? dd : 1; 25 else 26 day = ( dd >= 1 && dd <= days[ month ] ) ? dd : 1; 27 } // end function setDate 28 29 // overloaded prefix increment operator 30 Date &Date::operator++() 31 { 32 helpIncrement(); // increment date 33 return *this; // reference return to create an lvalue 34 } // end function operator++ 35 36 // overloaded postfix increment operator; note that the 37 // dummy integer parameter does not have a parameter name 38 Date Date::operator++( int ) 39 { 40 Date temp = *this; // hold current state of object 41 helpIncrement(); 42 43 // return unincremented, saved, temporary object 44 return temp; // value return; not a reference return 45 } // end function operator++ 46 47 // add specified number of days to date 48 const Date &Date::operator+=( int additionalDays ) 49 { 50 for ( int i = 0; i < additionalDays; i++ ) 51 helpIncrement(); 52 53 return *this; // enables cascading 54 } // end function operator+= 55 56 // if the year is a leap year, return true; otherwise, return false 57 bool Date::leapYear( int testYear ) const 58 { 59 if ( testYear % 400 == 0 || 60 ( testYear % 100 != 0 && testYear % 4 == 0 ) ) 61 return true; // a leap year 62 else 63 return false; // not a leap year 64 } // end function leapYear 65 66 // determine whether the day is the last day of the month 67 bool Date::endOfMonth( int testDay ) const 68 { 69 if ( month == 2 && leapYear( year ) ) 70 return testDay == 29; // last day of Feb. in leap year 71 else 72 return testDay == days[ month ]; 73 } // end function endOfMonth 74 75 // function to help increment the date 76 void Date::helpIncrement() 77 { 78 // day is not end of month 79 if ( !endOfMonth( day ) ) 80 day++; // increment day 81 else 82 if ( month < 12 ) // day is end of month and month < 12 83 { 84 month++; // increment month 85 day = 1; // first day of new month 86 } // end if 87 else // last day of year 88 { 89 year++; // increment year 90 month = 1; // first month of new year 91 day = 1; // first day of new month 92 } // end else 93 } // end function helpIncrement 94 95 // overloaded output operator 96 ostream &operator<<( ostream &output, const Date &d ) 97 { 98 static char *monthName[ 13 ] = { "", "January", "February", 99 "March", "April", "May", "June", "July", "August", 100 "September", "October", "November", "December" }; 101 output << monthName[ d.month ] << ' ' << d.day << ", " << d.year; 102 return output; // enables cascading 103 } // end function operator<<

Figure 11.14. Date class test program.

(This item is displayed on page 612 in the print version)

1 // Fig. 11.14: fig11_14.cpp 2 // Date class test program. 3 #include 4 using std::cout; 5 using std::endl; 6 7 #include "Date.h" // Date class definition 8 9 int main() 10 { 11 Date d1; // defaults to January 1, 1900 12 Date d2( 12, 27, 1992 ); // December 27, 1992 13 Date d3( 0, 99, 8045 ); // invalid date 14 15 cout << "d1 is " << d1 << " d2 is " << d2 << " d3 is " << d3; 16 cout << " d2 += 7 is " << ( d2 += 7 ); 17 18 d3.setDate( 2, 28, 1992 ); 19 cout << " d3 is " << d3; 20 cout << " ++d3 is " << ++d3 << " (leap year allows 29th)"; 21 22 Date d4( 7, 13, 2002 ); 23 24 cout << " Testing the prefix increment operator: " 25 << " d4 is " << d4 << endl; 26 cout << "++d4 is " << ++d4 << endl; 27 cout << " d4 is " << d4; 28 29 cout << " Testing the postfix increment operator: " 30 << " d4 is " << d4 << endl; 31 cout << "d4++ is " << d4++ << endl; 32 cout << " d4 is " << d4 << endl; 33 return 0; 34 } // end main  

d1 is January 1, 1900 d2 is December 27, 1992 d3 is January 1, 1900 d2 += 7 is January 3, 1993 d3 is February 28, 1992 ++d3 is February 29, 1992 (leap year allows 29th) Testing the prefix increment operator: d4 is July 13, 2002 ++d4 is July 14, 2002 d4 is July 14, 2002 Testing the postfix increment operator: d4 is July 14, 2002 d4++ is July 14, 2002 d4 is July 15, 2002  

Lines 1516 of main output each of the constructed Date objects, using the overloaded stream insertion operator (defined in Fig. 11.13, lines 96103). Line 16 of main uses the overloaded operator += to add seven days to d2. Line 18 uses function setDate to set d3 to February 28, 1992, which is a leap year. Then, line 20 preincrements d3 to show that the date increments properly to February 29. Next, line 22 creates a Date object, d4, which is initialized with the date July 13, 2002. Then line 26 increments d4 by 1 with the overloaded prefix increment operator. Lines 2427 output d4 before and after the preincrement operation to confirm that it worked correctly. Finally, line 31 increments d4 with the overloaded postfix increment operator. Lines 2932 output d4 before and after the postincrement operation to confirm that it worked correctly.

Overloading the prefix increment operator is straightforward. The prefix increment operator (defined in Fig. 11.13, lines 3034) calls utility function helpIncrement (defined in Fig. 11.13, lines 7693) to increment the date. This function deals with "wraparounds" or "carries" that occur when we increment the last day of the month. These carries require incrementing the month. If the month is already 12, then the year must also be incremented and the month must be set to 1. Function helpIncrement uses function endOfMonth to increment the day correctly.

The overloaded prefix increment operator returns a reference to the current Date object (i.e., the one that was just incremented). This occurs because the current object, *this, is returned as a Date &. This enables a preincremented Date object to be used as an lvalue, which is how the built-in prefix increment operator works for fundamental types.

Overloading the postfix increment operator (defined in Fig. 11.13, lines 3845) is trickier. To emulate the effect of the postincrement, we must return an unincremented copy of the Date object. For example, that int variable x has the value 7, the statement

cout << x++ << endl;

outputs the original value of variable x. So we'd like our postfix increment operator to operate the same way on a Date object. On entry to operator++, we save the current object (*this) in temp (line 40). Next, we call helpIncrement to increment the current Date object. Then, line 44 returns the unincremented copy of the object previously stored in temp. Note that this function cannot return a reference to the local Date object temp, because a local variable is destroyed when the function in which it is declared exits. Thus, declaring the return type to this function as Date & would return a reference to an object that no longer exists. Returning a reference (or a pointer) to a local variable is a common error for which most compilers will issue a warning.

Категории