Initialising and updating classes whose members have types defined by other classes

I have been trying to get though the drill in chapter 9 of B.Stroustrup's text "Programming: Principles and practice using C++". The drill expects me to output the date defined using a date class and also output the date of the day after the previously initialized date.

This drill is divided into sections where each section tackles a different aspect of the problem of printing out dates and I have completed it up until the 4th section where this must be done. This is section 9.7.1 where I must output the date 25 of June 1978 using the date class. The month variable in the date class is an enum and year is also its own type. In the year class there is a test to see whether the year falls within a given range and should only output the year if it is not less than the lower limit and not greater than or equal to the upper limit (All shown in the "year" class in the block of code below).

I define a function called add_day() within the date class which is supposed to increase the month by 1 if the day integer variable is greater than 31 and eventually will need to increase the year by 1 if this is the case and the month is also December. (i.e. m = month::Dec;). Its function is also just to increase the day by 1 so that if I put add_day(today, 1) and today is 25/06/1978 then it will simply become 26/06/1978 (excuse me for using the English date format dd/mm/yyyy).

The year class is causing me a lot of strife as every time I try to update the year variable it says that the constructor in the year class lacks arguments or something of that nature. Just so I can perhaps tackle this one problem at a time here is the code I have hitherto written for this drill:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

#include <iostream>
#include <cmath>



enum class month{
Jan = 1, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
};

class Year{
    static const int min = 1800;
    static const int max = 2200;
public:
    class Invalid{};
    Year(int x) : y{x}{if(x<min||max<=x) throw Invalid{};}
    int year();
private:
    int y;
};

int Year::year()
  {
    return y;
  }


class Date{

  int d;
  Year y;
  month m;

  public:

      Date(int dd, month mm, Year yy);
      void add_day(Date& day, int n);

};

month operator++(month& m)
  {
      m =(m==month::Dec)? month::Jan : month(int(m)+1);
      return m;
  }



void Date::add_day(Date& day, int n)
  {

      d = d + n;
      if(d > 31)
        {
          d = 1;
          ++m;
        }
  }

Date::Date(int dd, month mm ,Year yy)
  {

      if( dd >= 1 && dd <= 31) d = dd;
      else d = 0;
      m = mm;
      y = yy;


  }


using namespace std;

int main()
  {
    Date today{25,month::Jun,Year{1978}};

 
    
    if(today.d() !=0 )
    cout<<"Today's date is "<<today.d()<<"/"<<today.m()<<"/"<<today.y()<<endl;

    else cout<<"Invalid date."<<endl;
    add_day(today,1);

   cout<<"Tomorrow's date is "<<today.d()<<"/"<<today.m()<<"/"<<today.y()<<endl;
    

    return 0;
  }


The compile fails and it says that it is due to the constructor having no matching function for call to Year::Year(). It gives Year::Year(int) as a candidate only I am still unsure how I can get this to work. It must have something to do with the test for whether or not the year falls within the min-max range but I got the code for the Year class directly from the textbook and copied it out.

Any ideas appreciated.

Thanks,
Simon.
Last edited on
today.d()
d is private in Date class so you can't access outside the class. SEE chapter 9.4.3

There are some more issues but best to fix the access problem first.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include <iostream>
#include <cmath>
using namespace std;//not good practice
//http://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice

enum class month{
Jan = 1, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
};
ostream& operator << (ostream& os, const month& mon)
{
    os << static_cast<int>(mon);//use static cast over C-style
    return os;
}

class Year{
    static const int min = 1800;
    static const int max = 2200;
public:
    class Invalid{};
    Year(){}
    Year(int x) : y{x}{if(x<min||max<=x) throw Invalid{};}
    int year();//variable name and class name very similar - bad practice
    friend ostream& operator << (ostream& os, const Year& yr);
    Year& operator ++ ();//also overload post-increment;

private:
    int y;
};

int Year::year()
  {//uniform indent code throughout (google Allman style)
    return y;
  }
  Year& Year::operator ++ ()
  {
      ++y;
      return *this;
  }
ostream& operator << (ostream& os, const Year& yr)
{
    os << yr.y;
    return os;
}

class Date{

  int d;
  Year y;
  month m;

  public:

      Date(int dd, month mm, Year yy);
      void add_day(const int n);
      int getDay()const {return d;}//need getters
      Year getYear()const {return y;}
      month getMonth()const {return m;}
};

month operator++(month& m)//also overload post-increment
  {
      m =(m==month::Dec)? month::Jan : month(static_cast<int>(m)+1);
      return m;
  }

void Date::add_day(const int n)
  {

      d = d + n;
      if(d > 31)
        {
          d = 1;
          ++m;
          if(static_cast<int>(m) > 12)
          {
              m = static_cast<month>(1);//also need to check turn of the year
              ++y;
          }
        }
  }

Date::Date(int dd, month mm ,Year yy)
//ideally this ctor should be deleted as you can still end up with date object with d = 0;
// have an isValid() function that checks the data first and then a psuedo ctor that will set up the Date object
  {

      if( dd >= 1 && dd <= 31) d = dd;
      else d = 0;
      m = mm;
      y = yy;
}

int main()
  {
    Date today{25,month::Jun,Year{1978}};

    if(today.getDay() !=0 )
    cout<<"Today's date is "<<today.getDay()<<"/"<<today.getMonth()<<"/"<<today.getYear()<<endl;

    else cout<<"Invalid date."<<endl;
    today.add_day(1);

   cout<<"Tomorrow's date is "<<today.getDay()<<"/"<<today.getMonth()<<"/"<<today.getYear()<<endl;


    return 0;
  }


Last edited on
Hi,

Thanks for that solution. It compiles well but I obviously for my own purposes have gone through and read the edits that you made to see if I can get any meaning out of it and apply this to any programming that I do in the future.

My interpretation of this is that you created a friend function in the year class which puts the y integer into the output stream (os) to make it accessible universally (not just within the year and date classes) and to enable it to be incremented by creating that operator definition for year for the ++ operator.

This is my own basic understanding of how the program works. Let me know if I am on the right track or if you have anything to add. Thanks again for taking the time to help me solve this problem.

Simon.
Last edited on
created a friend function in the year class which puts the y integer data-member of const Year object yr, which is passed by reference to the output stream (os) object os (also passed by reference) to make it accessible universally (not just within the year and date classes) to make it print to console in the case when std::cout is used as the std::ostream object. There are other ostream objects available like std::cerr, etc which have their specific uses and contexts. and to enable it to be incremented by creating that operator definition for year for the ++ operator the increment operator overload is a separate function and unrelated to the operator << overload
1
2
3
4
5
6
Date::Date(int dd, month mm ,Year yy) : d(dd), m(mm), y(yy)
{
  // Or, follow the pattern in Year where you provide your own type
  // to throw.
  if ( d < 0 || d > 31 ) throw std::invalid_argument{};
}


Topic archived. No new replies allowed.