Passing Private Variables in Classes - Why Is This Not Allowed? - c++

I have this class:
class Date
{
public:
Date(); // Sets a date of January 1, 2000
Date(int mm, int dd, int yyyy); // Sets a date with the passed arguments; automatically converts two-digit years as being AFTER the year 2000
Date after_period_of ( int days ) const; // Elapses an arbitrary amount of time, and returns a new date
string mmddyy () const; // Returns date in MM/DD/YY format (1/1/00)
string full_date () const; // Returns date in full format (January 1, 2000)
string month_name () const; // Returns the name of the month as a string;
private:
int days_in_february(int yr);
int month;
int day;
int year;
};
When I try to pass private variable year as an argument into days_in_february, I get the following error message:
passing ‘const Date’ as ‘this’ argument of ‘int Date::days_in_february(int)’ discards qualifiers
days_in_february is called in after_period_of, like this:
Date Date::after_period_of (int days_elapsed) const
{
int new_month;
int new_year = year; // tried copying 'year' to get around this issue, but it did not help
int days_into_new_month;
int max_days_in_month[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } ;
max_days_in_month[1] = days_in_february(new_year);
and again in the same function:
if (new_month == 13)
{
new_month = 1;
new_year += 1;
max_days_in_month[1] = days_in_february(new_year);
}
days_in_february simply returns the number 28 or 29 based on year that is passed into it. It does not attempt to manipulate anything outside of its own block.
I have even tried to pass a non-programmed variable into it (days_in_february(2000)) and I get the same error.I've tried moving that function into the public domain, but that didn't fix the issue either.
Why is this happening?
Why am I not allowed to do this?

Inside after_period_of, you cannot access non-constant functions; in particular, you cannot access days_in_february. You can fix this by also declaring the latter function const.

Related

Fixing the Date Class increment (C++)

I have made a Library system. But I just checked and my 'Date' is not working properly.
The primary task of this class
(1) Increment date
Now, I am having an issue in the increment date, it works fine when I am incrementing it to the values up to 25-30. But when I enter 90, it messes up.
void increment_date(int num)
{
int day;
int month_new;
setDay(getDay()+num);
if( getDay()>Days_per_Month[getMonth()] )
{
day=getDay()-Days_per_Month[getMonth()];
setDay(day);
setMonth(getMonth()+1);
if(Days_per_Month[getMonth()]>12)
{
month_new=1;
setMonth(month_new);
setYear(getYear()+1);
}
}
cout<<"Return Date: ";
Print_Date();
}
//the code below is outside the class.
const int Date:: Days_per_Month[13]={0,31,28,31,30,31,30,31, 31, 30, 31, 30, 31};
int Date::checkDay(int testday) //returntype classname :: funcname (parameteres)
{
//static const int Days_per_Month[13]={0,31,28,31,30,31,30,31, 31, 30, 31, 30, 31};
if(testday > 0 && testday <= Days_per_Month[Month])
return testday;
if ( Month==2 && testday==29 && (Year%400==0 || (Year%4==0 && Year%100!=0)) ) //for leap year
return testday;
cout<<"Day "<<testday<<" invalid. Set to day 1."<<endl;
return 1;
}
enter image description here
setDay(getDay()+num);
if( getDay()>Days_per_Month[getMonth()] )
{
day=getDay()-Days_per_Month[getMonth()];
setDay(day);
setMonth(getMonth()+1);
In the above code, you add num to the value returned by getDay(), and then you check to see if getDay() is greater than the number of days in the current month, and if it is, you try to correct the problem by subtracting the number of days in the current month, and then incrementing the month.
So far, so good, but what if getDay() is still greater than the number of days in the current month after you've done that subtraction? (e.g. what if the user enters 300 for the increment-count?) In that case, you need to do the whole thing again, and keep doing it until getDay() is small enough to be a valid day-within-the-month value. So really a while loop is called for, not just an if.

Changing date for certain amount of days function

In class where we learn C++, I was given an assignment to make a class "date". Now, there is a specific function that I am required to make but I really do not have idea how to approach this. Members of class are day, month and year. Function takes some integer that represents days, and is supposed to set a new date, that comes after that many days. For example, if date is (DD-MM-YY) 20.01.2015, and we pass as an argument 15, new date is 04.02.2015, the problem is that I have to consider how many days each month has(considering February has 28 days), and if argument is too big, that we pass into next year, create an exception that prints how many days are there until next year(considering year has 365 days). That is, if the date is 20.12.2010, and argument is greater than 11, it should print 11.
My attempt was using while, where I declared at the beginning that int k=0; and where argument of function is a, than I used while(k!=a), but body of the function got really confusing because I used just too many if conditions. Another thing that I tried is to overlap operator++ and that surely gives me simpler function, because than there is only one for loop inside it, but I am not solving the problem, because in that overlapped operator function I am still using many if conditions.
Is there some elegant way to do this? Code and explanation would be great! Thank you!
Simply identify what you need and then create it, like
static unsigned int Date::days_of_month(const Month month, const unsigned int year);
void Date::operator+=(const unsigned int days);
+= could look like
void Date::operator+=(const unsigned int days){
increase current day by days
check if current day too large
if so, increase month (and maybe year)
repeat until day legal
}
Concept is clear? Haven't build in your output on purpose to give you something to do, after all, it's an exercise. Also wrote this in pseudo-code on purpose for the same reason. Try to create some code and if you really can't proceed, I'll add actual code.
const int m_daysInMonth[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 }; // first for index offset (no "zero" month)
class date {
public:
int days=20, month=11, year=2012;
void addDays(int daysNum)
{
int n_days = days, n_month = month; //new result. not saved, if there is an exception
while (daysNum > 0)
{
int daysToNextMonth = m_daysInMonth[n_month] - n_days + 1; //days before needs to change motn index
if (daysToNextMonth < daysNum) // change month
{
daysNum -= daysToNextMonth;
n_month++;
if (n_month > 12)
{
int daysLeft = m_daysInMonth[month] - days ;
n_month = month + 1;
while (n_month <= 12)
{
daysLeft += m_daysInMonth[n_month];
n_month++;
}
throw daysLeft;
}
n_days = 1; // start of the month
}
else // set day in the month
{
n_days += daysNum;
daysNum = 0;
}
}
days = n_days;
month = n_month;
}
};

C++: proper format to initialize derived and base along

I have made a date class that can hold the month, the day, and the year. But now I have a problem.
I've used this date class with inheritance to store for example the dateOfBirth for person(receptionist, passenger, and etc) (A hotel) by the Ctor of person(in fact using Ctor of date class in the initialization list of person), but now I don't know what is the format which I should use in main to initialize a person?! Is there anyway that I can do it directly in main at all? Sorry I'm beginner. Any help appreciated.
Here's some parts of my code:
class date {
int month = 1;
vector<int>month_days{ 31,31,31,31,31,31,30,30,30,30,30,29 };
int day = 1;
int year = 1300;
//---------------------member functions:
public:
date(int m = 1, int d = 1, int y = 1300) {
if (m>0 && m <= 31) month = m;
if (d>0 && d <= 31) day = d;
if (y>0 && y <= 3000) year = y;
}
//some other code...
};
class person:public date {
protected:
string m_name;
string m_familyName;
string m_idNumber;
//---------------------member functions:
public:
person(string name, string familyName, string idNumber, date dateOfBirth)
: m_name(name),m_familyName(familyName),m_idNumber(idNumber),date(dateOfBirth)
{
}
//some other code...
};
This is what I typed in main and I know that it can't do the job:
person a("Michael", "Deoran", "05174084", (4,17,1998));
I checked the stored date for object "a" and its date returns 1,1,1300, nevertheless, I don't know what can do the job too.
(By the way, the reason I defined a date, separate from person, is that I overloaded the operator- in it to calculate the stay time of passenger)
All you need to do is change parentheses to braces (that is for the code to compile, not to be also well-designed and not producing -Wreorder warnings):
person a("Michael", "Deoran", "05174084", {4, 17, 1998});
- list initialization.
The version with parentheses is equivalent to (comma operator):
person a("Michael", "Deoran", "05174084", 1998);
I would think twice before providing default values for the parameters. You probably don't need them.

Limiting what values constructors can take without making client wait for program execution

Let's say we're developing an API for other programmers. Our API has a Date object for them to use, and we want to limit what values the month, day, and year can be to valid values, such as 1-12 for the month, 1-31 for the day, and 1900-2014 for the year. So to do this, we create separate Month, Day, and Year objects, all of which are passed to Date's constructor, which we call like so:
Date date(Month::Jan(), Day(1), Year(1980));
In order to ensure that Month can only be 1-12, we can create separate functions for each month, each of which returns a month object corresponding to that month, and to ensure that no one can create a month with the wrong value, we make Month's constructor private:
class Month {
public:
static Month Jan () { return Month(1);}
static Month Feb () { return Month(2);}
static Month Mar () { return Month(3);}
static Month Apr () { return Month(4);}
static Month May () { return Month(5);}
static Month Jun () { return Month(6);}
static Month Jul () { return Month(7);}
static Month Aug () { return Month(8);}
static Month Sep () { return Month(9);}
static Month Oct () { return Month(10);}
static Month Nov () { return Month(11);}
static Month Dec () { return Month(12);}
private:
Month (int n) : monthNum (n) {}
int monthNum;
};
However, for Day and Year, we can't have function names that only contain numbers like Year::1997() and we want this format to be intuitive to client developers, so syntaxes like Year::year1997() are out. Plus, we don't want to manually code a function for every possible year. Let's say years go back significantly far enough that this would be a problem, like back to 1900, for instance. So we'll stick with the Day(1) or Year(1997) format.
Given this, what's a good way to ensure that the constructed Day and Year can only return the range of values we want? One way is to throw an exception if invalid values are entered:
class Day {
public:
Day (int d) : dayNum(d) {
if(d <= 0 || d >= 31) {
throw "Invalid day";
}
}
private:
int dayNum;
};
But this generates an error during runtime, not during compile time, and we're building this for developers to use, so we want them to know as soon as possible when they've done something wrong, such as putting an invalid day or year (e.g. day 32 or year 3000). What's an effective way to ensure clients can only create Day or Year objects with valid values, without waiting until program execution to notify them?
Template may be an option. Check out this

Debug assertion failed message

For some reason, when I use the destructor for my Update class, a debug assertion fail message displays...
here is my Update class with some code omitted for brevity. Placed in a header file:
using namespace std;
class Update
{
private:
int day, month, year;
static const int FIELD_SIZE = 3, DEFAULT_DAY = 12, DEFAULT_MONTH = 12,
DEFAULT_YEAR = 1999, DAYS_IN_MONTH = 30, MONTHS_IN_YEAR = 12, DAYS_IN_YEAR = 365;
int * date;
public:
static int dateUpdate;
Update(int D, int M, int Y)
{
day = D;
if (day < 1 || day > DAYS_IN_MONTH)
day = DEFAULT_DAY;
month = M;
if (month < 1 || month > MONTHS_IN_YEAR)
month = DEFAULT_MONTH;
year = Y;
if (year < 1)
year = DEFAULT_YEAR;
date = new int [FIELD_SIZE];
date[0] = day, date[1] = month, date[2] = year;
dateUpdate++;
}
~Update()
{
delete [] date;
dateUpdate--;
}
};
and here is my tester class in a cpp file:
#include <iostream>
#include "Update.h"
int Update::dateUpdate = 0;
int main()
{
Update u1(29, 12, 2000);
u1.Update::~Update();
return 0;
}
I've read through other questions involving debug assertion failures but something tells me a debug assertion failure can occur in various ways. As a result, I have little clue as to why the error message is displaying for my code... Is there something wrong with my destructor as I suspect at the moment? Thank you so much for your help in advance!
The problem is because you are calling destructor explicitly:
u1.Update::~Update();
this way it is called twice causing undefined behaviour, I suppose delete [] date; is called twice, the second time on alreade freed memory.
Another problem in your code is that you are using bare pointer for you array:
int * date;
this is actually quite low level programming style in C++ and can cause lots of problems. You should implement class copy constructor and assignment operator (*) that will allocate new date array when your Update class will be copied, otherwise you will have again problems with multiple date pointer deletions.
The best way is to use vector like
std::vector<int> date;
(*) I think good link where rule of three (or since C++11 rule of five) that applies here is explained: Rule-of-Three becomes Rule-of-Five with C++11?
You should change this line:
date[0] = day, date[1] = month, date[2] = year;
To:
date[0] = day;
date[1] = month;
date[2] = year;
You are using the comma operator, which returns the result of the last expression. This is not the same as how the comma operates in an initialization.
Also, in your main function, you do not need to explicitly call the destructor.
Your date method can't handle January 31 nor December 31.
You can access the parameters of the function directly instead of making a copy in the function. For example: day = D; is not needed; access the parameter directly: if ((D < 1) ....
If you used unsigned integers you would not need to check for negative numbers. I've never experienced a negative day, month or year in my life.
Since this is C++ and not Java or C#, you don't need to dynamically allocate variables. So rather than using int * date you could use int date[3].