日期类的整体
class Date
{
public:
// 构造函数
Date(int year = 0, int month = 1, int day = 1);
// 打印函数
void Print() const;
// 日期+=天数
Date& operator+=(int day);
// 日期+天数
Date operator+(int day) const;
// 日期-=天数
Date& operator-=(int day);
// 日期-天数
Date operator-(int day) const;
// 前置++
Date& operator++();
// 后置++
Date operator++(int);
// 前置--
Date& operator--();
// 后置--
Date operator--(int);
// 日期的大小关系比较
bool operator>(const Date& d) const;
bool operator>=(const Date& d) const;
bool operator<(const Date& d) const;
bool operator<=(const Date& d) const;
bool operator==(const Date& d) const;
bool operator!=(const Date& d) const;
// 日期-日期
int operator-(const Date& d) const;
// 析构,拷贝构造,赋值重载可以不写,使用默认生成的即可
private:
int _year;
int _month;
int _day;
};
上述代码表示了这次要实现的类,包括了多种运算符的重载,以及构造函数,相似的操作符,我们只实现一个。
日期类的析构和拷贝赋值函数不用写用默认的,因为日期类用的都是内置类型,没有动态开辟的空间,所以不用考虑深拷贝的问题
构造函数
// 获取某年某月的天数
inline int getmonthday(int year, int month)
{
// 数组存储平年每个月的天数
static int dayArray[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int day = dayArray[month];
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
{
//闰年2月的天数
day = 29;
}
return day;
}
// 构造函数
Date::Date(int year, int month, int day)
{
// 检查日期的合法性
if (year >= 0
&& month >= 1 && month <= 12
&& day >= 1 && day <= getmonthday(year, month))
{
_year = year;
_month = month;
_day = day;
}
else
{
// 严格来说抛异常更好
cout << "非法日期" << endl;
cout << year << "年" << month << "月" << day << "日" << endl;
exit(1);
}
}
上述代码解析:
一.getmonthday:这个函数用来判断是哪一年的哪一个月有几天
细节:
1.用static或者全局变量开辟dayArray[13],好处在于:节省每次调用函数时重复制造dayArray[13]。
2.inline内联函数,因为在Date对象进行++ -- 还有构造函数等多个函数都要用到getmonthday,所以用内联在调用的地方直接展开,省去了调用函数时找地址的时间消耗。
二.构造函数:构造函数很简单,但对于日期而言,每个月没有32天,没有13月,所以在构造函数要对传入的参数进行审批。错了以后直接exit退出。
operator+=和operator-=
为什么二者要一起讲,因为可以二者可以复用。
+=
// 日期+=天数
Date& Date::operator+=(int day)
{
if (day<0)
{
// 复用operator-=
*this -= -day;
}
else
{
_day += day;
// 日期不合法,通过不断调整,直到最后日期合法为止
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month > 12)
{
_year++;
_month = 1;
}
}
}
return *this;
}
对于上述代码:为什么负数时我们复用-=,因为负数时,我们的日期是减法,但是在加等的逻辑中我们只考虑通过加法去改变日期,所以只考虑日期超限制的情况,如果减法倒退超过限制,我们不用单独写逻辑,而是直接复用-=的逻辑即可。
-=
// 日期-=天数
Date& Date::operator-=(int day)
{
if (day < 0)
{
// 复用operator+=
*this += -day;
}
else
{
_day -= day;
// 日期不合法,通过不断调整,直到最后日期合法为止
while (_day <= 0)
{
_month--;
if (_month == 0)
{
_year--;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
}
return *this;
}
对于上述代码:和+= 相同,-=只考虑下限不考虑上限。所以负数时复用+=。
记住:减法考虑下限,加法考虑上限,负数考虑复用
operator+
// 日期+天数
Date Date::operator+(int day) const
{
Date tmp(*this);// 拷贝构造tmp,用于返回
// 复用operator+=
tmp += day;
return tmp;
}
对于上述代码:
1.我们复用了+= 。
2.为什么要用拷贝构造:参考int 时的加法;如果只是单纯的+1那对i并不影响,而如果是将i + 1赋值给i 那才会对 i 造成影响。所以拷贝构造出一份新的对象,返回这个新对象。而不是在原对象上更改
int i = 0; i+1; i = i + 1;
operator- 和 + 同理。
前置++和后置++
和内置类型一样,前置++返回的是修改后的内容,后置++修改值,但是返回的是没++的内容。
前置++
// 前置++
Date& Date::operator++()
{
// 复用operator+=
*this += 1;
return *this;
}
可以看到我们之间返回了*this,并且直接更改的*this。
后置++
// 后置++
Date Date::operator++(int)
{
Date tmp(*this);// 拷贝构造tmp,用于返回
// 复用operator+=
*this += 1;
return tmp;
}
后置++因为我们要返回的值是修改前的,所以我们拷贝构造后返回tmp,但不能引用!!!局部变量后续会销毁,但是传值传出去的是复制体,所以要用传值返回
前置--和后置--同理。
比较运算符
operator==
这里以==为例,其他的符号只是逻辑不同但是代码是类似的。
但值得一提的有:>= 可以复用 operator> 和 operator==. <= 同理。
bool Date::operator==(const Date& d) const
{
return _year == d._year
&&_month == d._month
&&_day == d._day;
}
把该判断的都判断了就对了。
日期-日期
日期类的计算难点就在于,日期的进位,但是对于日期相减是有一个取巧的方法滴。
日期-日期,算天数,我们可以找到较大的日期,然后用较小的日期一直+1,直到 == 较大的日期。中间在用一个变量一直计算+了多少个1.就可以实现了。
// 日期-日期
int Date::operator-(const Date& d) const
{
Date max = *this;// 假设第一个日期较大
Date min = d;// 假设第二个日期较小
int flag = 1;// 此时结果应该为正值
if (*this < d)
{
// 假设错误,更正
max = d;
min = *this;
flag = -1;// 此时结果应该为负值
}
int n = 0;// 记录所加的总天数
while (min != max)
{
min++;// 较小的日期++
n++;// 总天数++
}
return n*flag;
}