C++: Constructors: Parent class parameters and setting values in new instance - c++

I'm attempting to create a date and time class using inheritance.
The parent class, Date, has variables month, day, and year.
The Date derived class, DateTime, has the same variables but with hour, minute, and second added. The DateTime instance, when created, must be passed a Date instance and with optional parameters hour, minute, and second. The Date parameter is a requirement. If the optional parameters are not passed, default values of 0 will be applied.
Is there a more efficient way to implement this? I feel like it is tedious to re-set the parameters by retrieving them using a function in the Date instance for the new DateTime instance.
DateTime::DateTime(Date passDate){
day = passDate.getDay();
month = passDate.getMonth();
year = passDate.getYear();
hour = 0;
minute = 0;
second = 0;
}
DateTime::DateTime(Date passDate, int hourSet){
day = passDate.getDay();
month = passDate.getMonth();
year = passDate.getYear();
hour = hourSet;
minute = 0;
second = 0;
}
DateTime::DateTime(Date passDate, int hourSet, int minuteSet){
day = passDate.getDay();
month = passDate.getMonth();
year = passDate.getYear();
hour = hourSet;
minute = minuteSet;
second = 0;
}

You can call parent class constructor and default parameter values to make your code much more concise:
DateTime::DateTime(const Date& passDate, int hourSet = 0, int minuteSet = 0):
Date(passDate) {
hour = hourSet;
minute = minuteSet;
second = 0;
}

You can use default constructor parameters in order to have a single constructor.
DateTime::DateTime(Date passDate, int hourSet=0, int minuteSet=0, int secondSet=0) {
day = passDate.getDay();
month = passDate.getMonth();
year = passDate.getYear();
hour = hourSet;
minute = minuteSet;
second = secondSet;
}
If any of the hourSet=0, minuteSet or secondSet parameters are omitted, they will automatically be replaced with 0.

You need to provide copy constructor and assignment operator in both classes with proper validation of input values.
With that you just need to implement getters/setters for both base and derived.

Related

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

Get age out of date structure

I'm writing a program, in which I have some competitors, which are defined by structure. I also have a date structure.
struct date{ int d; int m; int y;};
I must sort competitor by their age, and I have no idea how to get age of competitors, to compare it.
I also tried this:
void age(tekmovalec comp){
#define _CRT_SECURE_NO_WARNINGS
time_t theTime = time(NULL);
struct tm *aTime = localtime(&theTime);
int day = aTime->tm_mday;
int month = aTime->tm_mon + 1; // Month is 0 - 11, add 1 to get a jan-dec 1-12 concept
int year = aTime->tm_year + 1900;
int age1, age2;
if(comp.rojDat.m > month){
age1=year-comp.rojDat.y-1;
age2=(12-month) + comp.rojDat.m;
}else{
age1=year-comp.rojDat.y;
age2=12-comp.rojDat.m;
}
int age3 = age1*12 + age2;
comp.age=age3;
}
But it returns error that localtime is unsafe.
As you want to sort by date, you may want to take a look at std::sort. For comparison, you need to write a function comp that compares 2 competitors (you need just to compare their ages, using basic comparations between days, months and years).
bool comp(competitor a, competitor b){
if(a.age.y < b.age.y)
return true;
else if(a.age.y == b.age.y && a.age.m < b.age.m)
return true;
else if(a.age.y == b.age.y && a.age.m == b.age.m && a.age.d < b.age.d)
return true;
else
return false;
}
Note that this is not the best implementation (actually it is pretty messy), but it's purpose is to give you a hint, not to be used as it is.
Another way to achive this is to overload comparison operators for competitors.
There is no need for localtime. You do not need age; what you need is a way to compare age. To determine which of us is older, we do not need to know what year this is, all we need to know is which of us was born earlier.
Write a function that compares two birthdates:
bool operator<(const date &A, const date &B)
{
...
}
Then use that to build a function that compares two competitors, then use that to build a function that sorts competitors.
instead of localtime() which can cause issues (not thread safe), you can use localtime_r() which should not have warnings :
struct tm *localtime_r(const time_t *restrict timer, struct tm *restrict result);
struct tm mytime; // use this instead of aTime
localtime_r(&theTime, &myTime)
int day = myTime.tm_mday;
int month = myTime.tm_mon + 1; // Month is 0 - 11, add 1 to get a jan-dec 1-12 concept
int year = myTime.tm_year + 1900;
This might also be localtime_s()
I presume you are using Visual studio. _CRT_SECURE_NO_WARNINGS needs to be a preprocessor definition (i.e. Seen before any #includes or code)
Right click on the project, get the 'Properties' menu and find the C/C++ preprocessor definitions and put it thre.
To actually compare times, it might be easier if you use mktime after transforming your dat struct into a tm, but you'll have trouble if anyone was born before 1970.
See here

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].