John
John

Reputation: 21

How can I change the parameters of a default constructor?

I have a class called Date that controls the date.

I have a constructor Date(). If this constructor is uninitialized, a default date will be put in its place by a private, static data member called default_.

I have initialized default_ in Date.cpp as follows:

Date Date::default_{1, Month::January, 1900};

Where the first argument is an int for the day, second is an enum class called Month for the month, and third is an int for the year.

When I run this program, I can print the date and it shows the correct default date that has been set by default_.

Question:

I want to be able to change the default date with a function:

setDefaultDate(int day, Month month, int year)

How can I do this? I have tried to implement setDefaultDate() as follows but it is not working:

void Date::setDefaultDate(int day, Month month, int year)
{
    default_ = {day,month,year};
}

Code:

Date.h:

#ifndef DATE_H
#define DATE_H
// date.h
// class Date declaration

#include <cassert>
#include <iostream>
using namespace std;

enum class Month
{
   January = 1,
   February,
   March,
   April,
   May,
   June,
   July,
   August,
   September,
   October,
   November,
   December
};

class Date
{
public:
   // return the day of the month
   int   day () const;
   // return the month of the year
   Month month () const;
   // return the year
   int year () const;

Date();
   static void setDefaultDate(int day, Month month, int year);

private:

   int   day_;
   Month month_;
   int   year_;

   static Date default_;
};

// standalone function for printing the date
void printDate(const Date& date);

#endif

Date.cpp:

// date.cpp
// Implementation of the Date class

#include "date.h"

int Date::day() const
{
   return day_;
}

Month Date::month() const
{
   return month_;
}

int Date::year() const
{
   return year_;
}

// Note, this standalone function is not part of the Date class
void printDate(const Date& date)
{
   cout << date.day() << "/"
       // cast to an integer to allow the month to be sent to the stream
<< static_cast<int>(date.month()) << "/"
       << date.year()
       << endl;
}

Date Date::default_{1, Month::January, 1900};

void Date::setDefaultDate(int day, Month month, int year)
{
   default_ = {day,month,year};
}

Date::Date()
{
   day_ = default_.day();
   month_ = default_.month();
   year_ = default_.year();
}

Example

int main()
{
    auto date_1 = Date{};
    date_1.setDefaultDate(29, Month::September, 2020);

    printDate(date_1);
}

Expected output:
29/9/2020

Actual Output:
1/1/1900

It compiles, but the setDefaultDate() function is not working, and the default date is being outputted that was declared in default_.

Upvotes: 0

Views: 402

Answers (3)

arfneto
arfneto

Reputation: 1765

I will show you a modified version of your code that should do what you need to, if I understood it correctly. I changed your code a bit, in order to use a single file

java is not C++, C++ is not java. And static things in C++ goes not the java way. Maybe you are used to that.

One thing is in C++ you can set up the defaults running code just outside of main() and outside the class as

...
}; // class Date{}

int Date::default_day = 1;
int Date::default_month = 1;
int Date::default_year = 1900;

void printDate(const Date& date);
void setDefaultDate(int, int, int);

int main(void)
{
...

And so you have the first set of defaults loaded.

And if you do it inside main() it compiles but the linker will complain about all 3 of them as unresolved.

  • for setDefaultDate() you can just declare it as Date's friend
    friend void setDefaultDate(int, int, int);
  • a convenient and common alternative to PrintDate() is to declare another friend to Date, as
    friend ostream& operator<<(ostream&, Date&);

An example using your code

Program shows


just a date at start (should use defaults 1900,1,1)

printDate()
 d: 1 m: 1 y: 1900

Now sets default to 1901/3/2

printDate()
 d: 2 m: 3 y: 1901

Now declared new Date instance as d3(2019,8,7)

printDate()
 d: 7 m: 8 y: 2019

Now print all 3 dates using "<<" operator, redefined for Date class

printDate using insertion operator '<<'
        year is 1900 month is 1 year is 1900

printDate using insertion operator '<<'
        year is 1901 month is 3 year is 1901

printDate using insertion operator '<<'
        year is 2019 month is 8 year is 2019

main() is

int main(void)
{
    cout << "\njust a date at start (should use defaults 1900,1,1)\n";
    Date d1;
    printDate(d1);
    cout << "\nNow sets default to 1901/3/2\n";
    setDefaultDate(2, 3, 1901);
    Date d2;
    printDate(d2);
    cout << "\nNow declared new Date instance as d3(2019,8,7)\n";
    Date d3(2019, 8, 7);
    printDate(d3);
    cout << "\nNow print all 3 dates using \"<<\" operator, redefined for Date class\n";
    cout << d1 << d2;
    cout << d3;
    return 0;
}
  • Code uses printDate() and also the overloaded << as an alternative to printDate(). I left two cout for the 3 Date just to show that you can chain the printing as with any other cout call.
  • The program sets the defaults before and inside main()
  • I added a 2nd constructor that accepts day, month and year as arguments

the complete example

#include <iostream>
using namespace std;

class Date
{
private:
    int  year_;
    int  month_;
    int  day_;

public:
    int year()  const { return year_;  };
    int month() const { return month_; };
    int day()   const { return day_;   };

    // default as Date d;
    Date() : 
        year_(default_year), month_(default_month), day_(default_day) {};
    // as Date d(2020,9,8)
    Date(int y, int m, int d) :
        year_(y), month_(m), day_(d){};

    friend void setDefaultDate(int, int, int);
    friend ostream& operator<<(ostream&, Date&);

private:
    static int  default_year;
    static int  default_month;
    static int  default_day;

}; // class Date{}

int Date::default_day = 1;
int Date::default_month = 1;
int Date::default_year = 1900;

void printDate(const Date& date);
void setDefaultDate(int, int, int);

int main(void)
{
    cout << "\njust a date at start (should use defaults 1900,1,1)\n";
    Date d1;
    printDate(d1);
    cout << "\nNow sets default to 1901/3/2\n";
    setDefaultDate(2, 3, 1901);
    Date d2;
    printDate(d2);
    cout << "\nNow declared new Date instance as d3(2019,8,7)\n";
    Date d3(2019, 8, 7);
    printDate(d3);
    cout << "\nNow print all 3 dates using \"<<\" operator, redefined for Date class\n";
    cout << d1 << d2;
    cout << d3;
    return 0;
}


void printDate(const Date& date)
{
    cout << "\nprintDate()\n" <<
        " d: " << date.day() <<
        " m: " << date.month() <<
        " y: " << date.year()
        << endl;
};

void setDefaultDate(int day, int month, int year)
{
    Date::default_day = day;
    Date::default_month = month;
    Date::default_year = year;
};

ostream& operator<<(ostream& out, Date& date)
{
    cout << "\nprintDate using insertion operator '<<'\n" <<
        "\tyear is " << date.year_ <<
        " month is " << date.month_ <<
        " year is " << date.year_ <<
        endl;
    return out;
};

No religion here. I compiled it just under MSVC 19.27

Upvotes: 0

Remy Lebeau
Remy Lebeau

Reputation: 597906

Your setDefaultDate() method sets only the value of default_, which main() has already made a copy of when calling the Date() constructor for the date_1 object. Changing the value of default_ afterwards has no effect whatsoever on the value of date_1. When you print date_1, you output the value it was initialized with. Changing the value of default_ will only have an effect on subsequent Date objects that you create afterwards.

If you want to change the value of date_1, you need to either:

  1. call setDefaultDate() before calling Date():
int main()
{
    Date::setDefaultDate(29, Month::September, 2020);
    Date date_1;
    printDate(date_1); // 29/9/2020
}
  1. add non-static methods to mutate the values of a Date object. You should also add a non-default constructor that can take a user-specified date as input, eg:
class Date
{
public:
   Date();
   Date(int initialDay, Month initialMonth, int initialYear);

   ...

   void setDay (int newValue);
   void setMonth (Month newValue);
   void setYear (int newValue);

   ...
};
Date::Date() :
   Date(default_.day_, default_.month_, default_.year_)
{
}

Date::Date(int initialDay, Month initialMonth, int initialYear) :
   day_(initialDay),
   month_(initialMonth),
   year_(initialYear)
{
}

void Date::setDay (int newValue)
{
   day_ = newValue;
}

void Date::setMonth (Month newValue)
{
   month_ = newValue;
}

void Date::setYear (int newValue)
{
   year_ = newValue;
}

...
int main()
{
    Date date_1;
    printDate(date_1); // 1/1/1900

    date_1.setDay(29);
    date_1.setMonth(Month::September);
    date_1.setYear(2020);
    printDate(date_1); // 29/9/2020

    date_1 = Date(9, Month::September, 2020);
    printDate(date_1); // 9/9/2020

    Date date_2(31, Month::December, 2020);
    printDate(date_2); // 31/12/2020

    Date::setDefaultDate(1, Month::April, 2020);

    Date date_3;
    printDate(date_3); // 1/4/2020
}

Upvotes: 0

Lukas-T
Lukas-T

Reputation: 11360

Let's step through your main line by line:

auto date_1 = Date{};

What will this do? It will call Date::Date, which will read all of the values from default_ into date_1. At this point default_ is 1/1/1900 and date_1 now is the same. Then

date_1.setDefaultDate(29, Month::September, 2020);

will change default_ and default_ only. I'm not sure why you expect date_1 to change here, maybe because you've called it like a non-static member function? You probably meant to change the default date before reading from it. You can do it like so:

int main()
{
    // call it like static function
    Date::setDefaultDate(29, Month::September, 2020);

    // Only then read from it.
    auto date_1 = Date{};
    
    // prints 29/9/2020
    printDate(date_1); 
}

Upvotes: 1

Related Questions