今天要向各位小伙伴们介绍的是c++入门必学之一的日期类:
在日常的生活中,日期对于我们来说是密不可分的,比如说工作上的时限,学业上的安排,对此,日期的计算就显得尤为重要了。(废话不多说)今天我们就来实现一个简易的日期计算器。(⬇️ ⬇️ )
方法:通过自定义一个日期类可以来实现简易的日期计算器。
第一步:我们要定义一个日期类,包括了成员函数的声明和成员变量的定义(在头文件中实现)。
第二步:我们要定义成员函数。
第三步:对成员函数进行测试和优化。
根据我们要实现的功能:
1.可以输入一个合法的日期(若没有输入,则进行默认赋值)
2.在日期上加上一定的天数(减少一定的天数)
3.将两个日期进行比较以及加减
先上手自定义一个日期类,代码⬇️ ⬇️ (头文件):
#pragma once
#include
using std::cout;
using std:: endl;
using std::cin;
class Date
{
public:
//定义构造函数
Date(int year = 0, int month = 1, int day = 1);
//打印日期
void Print();
//运算符重载
Date operator+(int day);
Date operator-(int day);
Date& operator+=(int day);
Date& operator-=(int day);
bool operator<=(const Date& d);
bool operator>=(const Date& d);
bool operator>(const Date& d);
bool operator<(const Date& d);
bool operator==(const Date& d);
bool operator!=(const Date& d);
Date& operator++();//前置
Date operator++(int);//后置
Date& operator--();//前置
Date operator--(int);//后置
int operator-(Date d);//计算两个日期的差值
private:
int _year;
int _month;
int _day;
};
1.可以输入一个合法的日期(若没有输入,则进行默认赋值)
自定义构造函数
对于这类默认成员函数,我们一般把它写成全缺省形式:
代码⬇️ ⬇️ (GetMonthDay函数和构造函数):
//多次使用,写成内联函数
inline int GetMonthDay(int year,int month)
{
//用static来创建变量(将变量存放到静态区),防止多次调用该函数时创建大量栈帧,消耗大
static int _monthArray[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
int day = _monthArray[month];
//先进行month的判断,因为运算中除法的效率是最低的
if (month == 2 && (year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
{
day = 29;
}
return day;
}
Date::Date(int year , int month, int day)
{
_year = year;
_month = month;
_day = day;
//要判断输入的日期是否合法
if (year<0||month <= 0 ||month >= 13 || day<=0 ||
day > GetMonthDay(_year, _month))
{
printf("非法日期,请重新输入\n");
exit(-1);
}
}
在构造函数中,我们写入一个判断,当输入的数据不符合规则时,我们就将程序终止。
注意❗️ ❗️
这里要对日期进行判断时,有一个天数要求:不能超过当月的天数。
所以这里我们就要定义一个GetMonthDay的函数来获得月天数。
细节优化:
(1).对于GetMonthDay函数,我们将它写成内联函数,因为我们后面还会多次 调用它。
(2).创建_monthArray数组时,我们要使用static,将它放到静态区。避免多次调用创建大量栈帧,消耗过大。
(3).关于闰年的判断,我们要将month==2这个条件前置,因为在运算过程中除法的效率较低。
2.在日期上加上一定的天数(减少一定的天数)
(1).对+=和-=进行重载
代码⬇️ ⬇️ :
//减少拷贝构造带来的消耗
Date& Date::operator+=(int day)
{
if (day < 0)
{
*this -= -day;
}
else {
_day += day;
//判断天数是否超越了当月的天数
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
//判断月数是否超过了12
if (_month > 12)
{
_year++;
_month = 1;
}
}
}
return *this;
}
//减少拷贝构造带来的消耗
Date& Date::operator-=(int day)
{
if (day < 0)
{
*this += -day;
}
else {
_day -= day;
//判断天数是否小于0
while (_day <= 0)
{
//判断月数是否等于一
if (_month == 1)
{
--_year;
_month = 12;
_day += GetMonthDay(_year, _month);
}
else
{
--_month;
_day += GetMonthDay(_year, _month);
}
}
}
return *this;
}
注意❗️ ❗️
当输入的时候实参day为负数, 我们就要进行反向的运算(如加上一个负数等于减去它的相反数)。
细节优化:
(1).返回用传引用的方式进行(因为返回值是定义的对象,当前函数结束后它依然存在),可以避免拷贝构造,避免创建临时变量,减少消耗。
(2).进行反向运算时,我们要学会复用已有的+=(-=)重载函数。
(2).对+和-进行重载
对于+(-)重载而言,它们与+=(-=)最大的区别就在于:+(-)它们不会改变原对象,即返回用传值方式;而+=(-=)它们会改变原对象,所以对于他们的返回,用传引用返回。
代码⬇️ ⬇️ :
Date Date::operator+(int day)
{
//构造一个临时变量,避免改变对象
Date tmp(*this);
//直接复用+=
tmp += day;
//tmp._day += day;
判断天数是否大于当月的天数
//while (tmp._day > GetMonthDay(tmp._year, tmp._month))
//{
// tmp._day -= GetMonthDay(tmp._year, tmp._month);
// tmp._month++;
// //判断月数是否大于12
// if (tmp._month > 12)
// {
// tmp._year++;
// tmp._month = 1;
// }
//}
return tmp;
}
Date Date::operator-(int day)
{
Date tmp(*this);
//直接复用-=
tmp -= day;
//tmp._day -= day;
判断天数是否小于0
//while (tmp._day < 0)
//{
// //判断月是否为一月
// if (tmp._month == 1)
// {
// tmp._year--;
// tmp._month = 12;
// tmp._day += GetMonthDay(tmp._year, tmp._month);
// }
// else
// {
// tmp._month--;
// tmp._day += GetMonthDay(tmp._year, tmp._month);
// }
//}
return tmp;
}
细节优化:
充分复用+=(-=),来避免重复的代码。
(3).对前后自增(自减)进行重载
对于自增(自减)的重载,我们要区分前置和后置的区别。为了区分,在语法中给后置重载函数的参数加上类型。
前置时,我们要对原对象直接进行加一(减一)操作,返回原对象;后置时,我们需要定义一个临时变量,对原对象进行加一(减一)操作,但是返回的是临时变量。
代码⬇️ ⬇️ :
Date& Date::operator++()//前置
{
return *this += 1;;
}
Date Date::operator++(int)//后置
{
Date tmp(*this);
*this += 1;
return tmp;
}
Date& Date::operator--()//前置
{
return *this -= 1;
}
Date Date::operator--(int)//后置
{
Date tmp(*this);
*this -= 1;
return tmp ;
}
细节优化:
(1).前置时,使用传引用返回,避免拷贝构造。
(2).加一(减一)直接复用+=(-=)函数。
3.将两个日期进行比较
(1).对==进行重载
(2).对!=进行重载
(3).对>(<)进行重载
(4).对>=进行重载
(5).对<=进行重载
这里我们采用了布尔类型,使用const避免输入型参数(传引用的d)被错误修改。
代码⬇️ ⬇️ :
//复用>和==,来表示其他的运算符
bool Date::operator>(const Date& d)
{
if (_year > d._year)
return true;
else if (_year < d._year)
return false;
else//_year < d._year
{
if (_month > d._month)
return true;
else if (_month < d._month)
return false;
else//_month < d._month
{
if (_day > d._day)
return true;
else
return false;
}
}
}
bool Date::operator<(const Date& d)
{
return !(*this > d || *this == d);
// if (_year > d._year)
// return false;
// else if (_year < d._year)
// return true;
// else//_year < d._year
// {
// if (_month > d._month)
// return false;
// else if (_month < d._month)
// return true;
// else//_month < d._month
// {
// if (_day < d._day)
// return true;
// else
// return false;
// }
// }
}
bool Date::operator>=(const Date& d)
{
return *this > d || *this == d;
}
bool Date::operator<=(const Date& d)
{
return !(* this > d);
}
bool Date::operator==(const Date& d)
{
if (_year == d._year)
{
if (_month == d._month)
{
if (_day == d._day)
return true;
}
}
return false;
}
bool Date::operator!=(const Date& d)
{
//复用==
return !(*this == d);
//if (_year == d._year)
//{
// if (_month == d._month)
// {
// if (_day == d._day)
// return false;
// }
//}
// return true;
}
细节优化:
(1).形参采用了传引用的方式避免了拷贝构造。
(2).学会使用复用>(<)和==来重载其他运算符
(6).计算两个日期的差值
对-进行重载,使其既可以返回日期,也可以返回天数差值。
代码 ⬇️ ⬇️ :
int Date::operator-(Date d)
{
//定义一个变量用于计数
int count = 0;
//定义一个对象防止改变原对象
Date tmp(*this);
if (tmp < d)
{
while (tmp != d)
{
++count;
tmp += 1;
}
}
else if (tmp > d)
{
while (tmp != d)
{
--count;
tmp -= 1;
}
}
return count;
}
细节优化:充分使用复用,减少代码量。
愿与诸君共勉!