10.8 Case Study: A Date Class

The program of Figs. 10.610.8 demonstrates a Date class, which 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 (Fig. 10.6) specifies that Date’s public interface includes an overloaded stream insertion operator (line 10), a default constructor (line 12), a setDate function (line 13), an overloaded prefix increment operator (line 14), an overloaded postfix increment operator (line 15), an overloaded += addition assignment operator (line 16), a function to test for leap years (line 17) and a function to determine whether a day is the last day of the month (line 18).

Fig. 10.6 Date class definition with overloaded increment operators.

Alternate View

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


Fig. 10.7 Date class member- and friend-function definitions.

Alternate View

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

Fig. 10.8 Date class test program.

Alternate View

 1   // Fig. 10.8: fig10_08.cpp
 2   // Date class test program.
 3   #include <iostream>
 4   #include "Date.h" // Date class definition
 5   using namespace std;
 6
 7   int main() {
 8      Date d1{12, 27, 2010}; // December 27, 2010
 9      Date d2; // defaults to January 1, 1900
10
11      cout << "d1 is " << d1 << "
d2 is " << d2;
12      cout << "

d1 += 7 is " << (d1 += 7);
13
14      d2.setDate(2, 28, 2008);
15      cout << "

 d2 is " << d2;
16      cout << "
++d2 is " << ++d2 << " (leap year allows 29th)";
17
18      Date d3{7, 13, 2010};
19
20      cout << "

Testing the prefix increment operator:
"
21         << " d3 is " << d3 << endl;                        
22      cout << "++d3 is " << ++d3 << endl;                   
23      cout << " d3 is " << d3;                              
24
25      cout << "

Testing the postfix increment operator:
"
26         << " d3 is " << d3 << endl;                         
27      cout << "d3++ is " << d3++ << endl;                    
28      cout << " d3 is " << d3 << endl;                       
29   }

d1 is December 27, 2010
d2 is January 1, 1900

d1 += 7 is January 3, 2011

  d2 is February 28, 2008
++d2 is February 29, 2008 (leap year allows 29th)

Testing the prefix increment operator:
  d3 is July 13, 2010
++d3 is July 14, 2010
  d3 is July 14, 2010

Testing the postfix increment operator:
  d3 is July 14, 2010
d3++ is July 14, 2010
  d3 is July 15, 2010

Function main (Fig. 10.8) creates two Date objects (lines 8–9)—d1 is initialized to December 27, 2010 and d2 is initialized by default to January 1, 1900. The Date constructor (defined in Fig. 10.7, lines 13–15) calls setDate (defined in Fig. 10.7, lines 18–42) to validate the month, day and year specified. Invalid values for the month, day or year result in invalid_argument exceptions.

Line 11 of main (Fig. 10.8) outputs each Date object, using the overloaded stream insertion operator (defined in Fig. 10.7, lines 105–111). Line 12 of main uses the overloaded operator += (defined in Fig. 10.7, lines 61–67) to add seven days to d1. Line 14 in Fig. 10.8 uses function setDate to set d2 to February 28, 2008, which is a leap year. Then, line 16 preincrements d2 to show that the date increments properly to February 29. Next, line 18 creates a Date object, d3, which is initialized with the date July 13, 2010. Then line 22 increments d3 by 1 with the overloaded prefix increment operator. Lines 20–23 output d3 before and after the preincrement operation to confirm that it worked correctly. Finally, line 27 increments d3 with the overloaded postfix increment operator. Lines 25–28 output d3 before and after the postincrement operation to confirm that it worked correctly.

Date Class Prefix Increment Operator

Overloading the prefix increment operator is straightforward. The prefix increment operator (defined in Fig. 10.7, lines 45–48) calls utility function helpIncrement (defined in lines 86–102) to increment the date. This function deals with “wraparounds” or “carries” that occur when we increment past a month’s last day, which requires 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 determine whether the end of a month has been reached and 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.

Date Class Postfix Increment Operator

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


cout << x++ << endl;

outputs the original value of x. 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 53). Next, we call helpIncrement to increment the current Date object. Then, line 57 returns temp—the unincremented copy of the original Date object. This function cannot return a reference to the local Date object temp, because a local variable is destroyed when the function in which it’s declared exits. Thus, declaring the return type to this function as Date& would return a reference to an object that no longer exists.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset