日期类的实现主要是练习运算符重载,只要是对于日期的计算有意义的运算符都应该重载。
注:本篇文章的日期只考虑公元后的。
在中篇我们已经实现了==
和<
的重载:
==
比较两个日期是否相同
//.cpp
bool Date::operator==(const Date& d) const
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
<
判断左值日期是否在右值日期的前面
//.cpp
bool Date::operator<(const Date& d) const
{
return ((_year < d._year)
|| (_year == d._year && _month < d._month)
|| (_year == d._year && _month == d._month && _day < d._day));
}
构造函数应该对用户传进来的日期初始化进行检查
为此,我们首先要判断年月日是否在合法的范围,要判断day
的范围,需要GetMonthDay
函数获得这个月的天数,同时还要用isLeapYear
判断是否是闰年,因为isLeapYear
函数较短,所以可以直接写在类里面作为内联。
//.h
bool isLeapYear(int year)
{
return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
}
//.cpp
int Date::GetMonthDay(int year, int month)
{
assert(year >= 1 && month >= 1 && month <= 12);
static int monthDayArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
if (month == 2 && isLeapYear(year))
return 29;
else
return monthDayArray[month];
}
Date::Date(int year, int month, int day)
{
if (year >= 1
&& month <= 12 && month >= 1
&& day >= 1 && day <= GetMonthDay(year, month))
{
_year = year;
_month = month;
_day = day;
}
else
{
cout << "日期非法" << endl;
}
}
<=
>
>=
!=
我们已经写好了<
和==
的重载,建议直接复用这两个来实现
bool operator<=(const Date& d) const
{
return *this < d || *this == d;
}
重载>
复用<=
的重载即可
bool operator>(const Date& d) const
{
return !(*this <= d);
}
建议复用<
,可以少调用一个函数
bool operator>(const Date& d) const
{
return d < *this;
}
重载>=
复用<
的重载即可
bool operator>=(const Date& d) const
{
return !(*this < d);
}
复用<=
也可以,但是多调用一个函数
bool operator>=(const Date& d) const
{
return d <= *this;
}
重载!=
复用==
bool operator!=(const Date& d) const
{
return !(*this == d);
}
因为上面4个函数都比较短小,所以建议直接定义到类里面。
+=
+
-=
-
天数由于+=是直接在原对象上+,所以我们先实现+=
重载+=
让一个日期对象+=天数。
思路是直接把day
加到_day
上,如果_day
不合法,那么就进位,如果月份不合法,就继续进位。
为了支持连续的+=,要有返回值。
Date& Date::operator+=(int day)
{
if (day < 0)
return *this -= -day;
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month == 13)
{
_year++;
_month = 1;
}
}
return *this;
}
👆:如果有人传入了负数的day
呢?
传入负数的day
,_day -= day
有可能小于1,也进不去下面的while循环进行处理,最终导致结果错误。当然-=
也面临同样的问题,
注意到+=一个负数就是-=这个负数的相反数,我们接下来实现-=
,然后让它们相互复用即可。
重载+
复用+=
,并且不能改变原对象,
这里就拷贝构造一个中间对象ret
,返回ret
必须传值返回,因为ret
出作用域会被销毁。
Date Date::operator+(int day) const
{
Date ret(*this);
ret += day;
return ret;
}
不建议先写+
的重载,然后重载+=
的时候复用+
,因为+
肯定是需要拷贝构造的,如果+=
复用了+
,那么+=
也要调用拷贝构造,影响效率。
重载-=
-
同上,先实现-=
,-
直接复用-=
_day -= day
,如果_day不合法,则需要向前借位。
Date& Date::operator-=(int day)
{
if (day < 0)
return *this += -day;
_day -= day;
while (_day < 1)
{
_month--;
if (_month == 0)
{
_month = 12;
_year--;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
Date Date::operator-(int day) const
{
Date ret(*this);
ret -= day;
return ret;
}
++
--
前置++
和后置++
都是单操作数,它们的重载要如何区分呢?
要区分就需要构成函数重载,C++语法规定,增加一个int
类型额外参数的是后置++
(注意:额外参数必须是int
类型)
Date& operator++() //前置++
{
*this += 1;
return *this;
}
Date operator++(int) //后置++
{
Date tmp(*this);
*this += 1;
return tmp;
}
Date& operator--() //前置--
{
*this -= 1;
return *this;
}
Date operator--(int) //后置--
{
Date tmp(*this);
*this -= 1;
return tmp;
}
调用的时候++d
等价于d.operator++()
,d++
等价于d.operator++(0)
,编译器会自动传入一个额外参数表示调用后置++。这个参数本身没有意义,所以形参部分可以省略变量名,只写int
小知识:通过++和–的重载我们可以发现,前置的效率高一些,因为后置多了两个拷贝构造。
-
(计算日期间隔天数)我们再重载一个-
,只不过这里是日期减日期,表示两个日期间隔天数。
int Date::operator-(const Date& d) const
{
int flag;
Date max, min;
if (*this > d)
{
max = *this;
min = d;
flag = 1;
}
else
{
max = d;
min = *this;
flag = -1;
}
int n = 0;
while (min != max)
{
++n;
++min;
}
return n * flag;
}
👆:先看左右操作数哪个更大,并用flag控制正负号,让小日期一直++到大日期,统计的次数即为两个日期的间隔天数。
#pragma once
#include <iostream>
#include <cassert>
using std::cout;
using std::cin;
using std::endl;
class Date
{
public:
bool isLeapYear(int year)
{
return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
}
int GetMonthDay(int year, int month);
Date(int year = 1, int month = 1, int day = 1);
void Print() const
{
cout << _year << "-" << _month << "-" << _day << endl;
}
bool operator==(const Date& d) const;
bool operator<(const Date& d) const;
bool operator<=(const Date& d) const
{
return *this < d || *this == d;
}
bool operator>(const Date& d) const
{
return d < *this;
}
bool operator>=(const Date& d) const
{
return !(*this < d);
}
bool operator!=(const Date& d) const
{
return !(*this == d);
}
Date& operator+=(int day);
Date operator+(int day) const;
Date& operator-=(int day);
Date operator-(int day) const;
Date& operator++() //前置++
{
*this += 1;
return *this;
}
Date operator++(int) //后置++
{
Date tmp(*this);
*this += 1;
return tmp;
}
Date& operator--() //前置--
{
*this -= 1;
return *this;
}
Date operator--(int) //后置--
{
Date tmp(*this);
*this -= 1;
return tmp;
}
int operator-(const Date& d) const;
private:
int _year;
int _month;
int _day;
};
#include "Date.h"
int Date::GetMonthDay(int year, int month)
{
assert(year >= 1 && month >= 1 && month <= 12);
static int monthDayArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
if (month == 2 && isLeapYear(year))
return 29;
else
return monthDayArray[month];
}
Date::Date(int year, int month, int day)
{
if (year >= 1
&& month <= 12 && month >= 1
&& day >= 1 && day <= GetMonthDay(year, month))
{
_year = year;
_month = month;
_day = day;
}
else
{
cout << "日期非法" << endl;
}
}
bool Date::operator==(const Date& d) const
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
bool Date::operator<(const Date& d) const
{
return ((_year < d._year)
|| (_year == d._year && _month < d._month)
|| (_year == d._year && _month == d._month && _day < d._day));
}
Date& Date::operator+=(int day)
{
if (day < 0)
return *this -= -day;
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month == 13)
{
_year++;
_month = 1;
}
}
return *this;
}
Date Date::operator+(int day) const
{
Date ret(*this);
ret += day;
return ret;
}
Date& Date::operator-=(int day)
{
if (day < 0)
return *this += -day;
_day -= day;
while (_day < 1)
{
_month--;
if (_month == 0)
{
_month = 12;
_year--;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
Date Date::operator-(int day) const
{
Date ret(*this);
ret -= day;
return ret;
}
int Date::operator-(const Date& d) const
{
int flag;
Date max, min;
if (*this > d)
{
max = *this;
min = d;
flag = 1;
}
else
{
max = d;
min = *this;
flag = -1;
}
int n = 0;
while (min != max)
{
++n;
++min;
}
return n * flag;
}
还有流插入<<
和流提取>>
的重载本篇文章未实现。因为涉及到友元,我们将在下篇作为例子实现。