
在学习了C++的6个默认成员函数后,我们现在动手实现一个完整的日期类,来加强对这6个默认成员函数的认识。 这是日期类中所包含的成员函数和成员变量:
// 获取某年某月的天数
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;
}
}void Date::Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}对于**+=**运算符,我们先将需要加的天数加到日上面,然后判断日期是否合法,若不合法,则通过不断调整,直到日期合法为止。 调整日期的思路:
反复执行1和2,直到日期合法为止。
如图:

// 日期 += 天数
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;
}+运算符的重载,要注意:虽然我们返回的是加了之后的值,但是对象本身的值并没有改变。就像a = b + 1,b + 1的返回值是b + 1,但是b的值并没有改变。所以我们可以用this指针指向的对象。
// 日期 + 天数
Date Date::operator +(int day)
{
Date tmp(*this);
tmp += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
++_month;
if (_month > 12)
{
_year++;
_month = 1;
}
}
return tmp;
}
但是日期 + 天数 的函数可能会觉得冗余,我们可以直接采用复用,且用const对该函数进行修饰,防止函数内部改变了this指针指向的对象。如下:
// 日期 + 天数
Date Date::operator +(int day)const
{
Date tmp(*this);
tmp += day;
return tmp;
}注意:+=运算符的重载函数采用的是引用返回,因为出了函数作用域,this指针指向的对象没有被销毁。但+运算符的重载函数的返回值只能是传值返回,因为出了函数作用域,对象tmp就被销毁了,不能使用引用返回。
对于-=运算符,我们先用日减去需要减的天数,然后判断日期是否合法,若不合法,则通过不断调整,直到日期合法为止。 调整日期的思路:
反复执行1、2和3,直到日期合法为止。

// 日期 -= 天数
Date& Date::operator -=(int day)
{
if (day < 0)
{
// 复用operator+=
*this += -day;
}
else
{
_day -= day;
while (_day <= 0)
{
_day += GetMonthDay(_year, _month);
--_month;
if (_month == 0)
{
--_year;
_month = 12;
}
}
}
return *this;
}和+运算符的重载类似,我们可以复用上面已经实现的-=运算符的重载函数,而且最好用const对该函数进行修饰,防止函数内部改变了this指针指向的对象。
// 日期 - 天数
Date Date::operator -(int day)const
{
Date tmp(*this);
tmp -= day;
return tmp;
}
注意:-=运算符的重载函数采用的是引用返回,但-运算符的重载函数的返回值只能是传值返回,也是由于-运算符重载函数中的tmp对象出了函数作用域被销毁了,所以不能使用引用返回。

// 前置++
Date& Date::operator++()
{
// 复用operator+=
*this += 1;
return *this;
}// 后置++
Date Date::operator++(int)
{
Date tmp(*this);// 拷贝构造tmp,用于返回
// 复用operator+=
*this += 1;
return tmp;
}注意: 后置++也是需要返回加了之前的值,只能先用对象tmp保存之前的值,然后再然对象加一,最后返回tmp对象。由于tmp对象出了该函数作用域就被销毁了,所以后置++只能使用传值返回,而前置++可以使用引用返回。

// 前置--
Date& Date::operator--()
{
// 复用operator-=
*this -= 1;
return *this;
}// 后置--
Date Date::operator--(int)
{
Date tmp(*this);// 拷贝构造tmp,用于返回
// 复用operator-=
*this -= 1;
return tmp;
}
日期类的大小关系比较需要重载的运算符看起来有6个,实际上我们只用实现两个就可以了,然后其他的通过复用这两个就可以实现。
注意: 进行日期的大小比较,我们并不会改变传入对象的值,所以这6个运算符重载函数都应该被const所修饰。
运算符的重载很简单,先判断年是否大于,再判断月是否大于,最后判断日是否大于,这其中有一者为真则函数返回true,否则返回false。
bool Date::operator>(const Date& d) const
{
if (_year > d._year)
{
return true;
}
else if (_year == d._year)
{
if (_month > d._month)
{
return true;
}
else if (_month == d._month)
{
if (_day > d._day)
{
return true;
}
}
}
return false;
}==运算符的重载也是很简单,年月日均相等,则为真。
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 *this > d || *this == d;
}<,大于等于的反面即是小于。
bool Date::operator<(const Date& d) const
{
return !(*this >= d);
}<=,大于的返回即是小于等于。
bool Date::operator<=(const Date& d) const
{
return !(*this > d);
}!=,等于的反面即是不等于。
bool Date::operator!=(const Date& d) const
{
return !(*this == d);
}日期 - 日期,即计算传入的两个日期相差的天数。我们只需要让较小的日期的天数一直加一,直到最后和较大的日期相等即可,这个过程中较小日期所加的总天数便是这两个日期之间差值的绝对值。若是第一个日期大于第二个日期,则返回这个差值的正值,若第一个日期小于第二个日期,则返回这个差值的负值。
// 日期-日期
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;
}