日期类是很常用的一个类,我们要模仿实现的就是日常生活中会用到的一些功能。
首先给出Date类的头文件:
class Date
{
friend std::ostream& operator<<(std::ostream& _cout, const Date& d);
friend std::istream& operator>>(std::istream& _cin, Date& d);
public:
Date(int year = 1970, int month = 1, int day = 1);
~Date();
Date(const Date& d);
int getYear() const;
int getMonth() const;
int getDay() const;
Date& operator=(const Date& d);
Date operator+(int days) const;
Date& operator+=(int days);
Date operator-(int days) const;
Date& operator-=(int days);
int operator-(const Date& d) const;
Date& operator++();
Date operator++(int);
Date& operator--();
Date operator--(int);
Date* operator&();
const Date* operator&() 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;
bool operator!=(const Date& d) const;
private:
bool isLeapYear() const;
int getDayInMonth() const;
int _year;
int _month;
int _day;
};
下面我们来一一实现它们:
//构造函数
//使用初始化参数列表,为成员变量一一赋值即可。
//当初始化完成后,用assert语句判断成员变量的合法性。
//如果不合法,返回异常。
Date::Date(int year, int month, int day)
:_year(year)
,_month(month)
,_day(day)
{
assert(this->_month < 13 && this->_month > 0
&& this->_day > 0 && this->_day < getDayInMonth());
}
//析构函数
//将成员变量赋值为0
Date::~Date()
{
_year = 0;
_month = 0;
_day = 0;
}
//拷贝构造函数
//这里的形参只能用引用类型。
//如果直接用的是对象类型,那么就会在传参的时候创建一个形参d,
//而d是用实参拷贝构造的,这样就会一直循环下去。
Date::Date(const Date& d)
:_year(d._year)
,_month(d._month)
,_day(d._day)
{}
//返回年份
int Date::getYear() const
{
return this->_year;
}
//返回月份
int Date::getMonth() const
{
return this->_month;
}
//返回日期
int Date::getDay() const
{
return this->_day;
}
//赋值运算符重载
//当 d 不是当前对象时,将d的成员变量的值,依次赋给当前对象的成员变量。
//并且返回当前对象的引用。
//由于这里当前对象的生命周期比函数长,因此可以直接返回当前对象的引用。
//省去了创建临时变量返回的步骤,提高了效率。
Date& Date::operator=(const Date& d)
{
if (this != &d) {
this->_year = d._year;
this->_month = d._month;
this->_day = d._day;
}
return *this;
}
//判断年份是否是闰年
//在Date类外,判断是否是闰年,可以由使用者自行判断。
//这里的作用是为了辅助实现其他功能,因此直接定义为私有,对外不可见。
bool Date::isLeapYear() const
{
return (this->_year % 400 == 0) || ((this->_year % 4 == 0) && (this->_year % 100 != 0));
}
//判断日期的合法性
//用来检测日期的合法性,确保日期是正确的。
//需要注意的就只有,当是闰年时,二月日期为29天。否则,为28天。
int Date::getDayInMonth() const
{
int daynumber[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if (this->isLeapYear()) {
daynumber[1] = 29;
}
return daynumber[this->_month - 1];
}
//+运算符重载
//实现的原理为:
//先将days全部加在成员变量_day上面,
//用.getDayInMonth()函数判断日期的合法性,
//当日期不正确时,从_day中减去本月的天数,将月份增加一月,
//一直循环,直到日期合法时,跳出循环,并且将计算完的对象返回。
//可以理解为:传进来一个总天数days,从当前日期往前走,每走一个月,那么从总日期中减去这个月的天数。
//然后所处的月份加一,然后从新的一月的第一天开始走。如果走完了一年,那么月份变为一月,年份加一。
//因为 + 运算符并不会 影响当前对象本身,所以因该将结果保存在临时对象中,
//又因为临时对象是创建在栈上的,所以函数运行完之后会销毁,
//因此不能返回引用,所以返回的是创建的临时对象的值。
Date Date::operator+(int days) const
{
Date temp(*this);
if (days < 0) {
return temp - (-days);
}
temp._day += days;
while(temp._day > temp.getDayInMonth()) { //日期不合法
temp._day -= temp.getDayInMonth();
temp._month++;
if (temp._month > 12) {
temp._year++;
temp._month = 1;
}
}
return temp;
}
//+=运算符重载
//+=运算符会影响当前对象,所以直接在函数中,将 + 运算符计算的结果赋值给当前对象
//然后返回引用即可
Date& Date::operator+=(int days)
{
(*this) = (*this) + days;
return *this;
}
//-运算符重载
//原理同上面+运算符,只是日期是倒着走
//实现的原理为:
//先用成员变量_day减去days
//当_day < 1 时,说明日期不合法。
//当日期不合法时,将月份减少一月,给_day加上上月的天数,说明往前退了一个月。
//一直循环,直到日期合法时,跳出循环,并且将计算完的对象返回。
//需要注意的是,在计算的时候,由于是往前退一个月,所以应该是先减去月份,再加上月份对应的日期,
//或者是先加上上月的日期,然后再减去月份。
Date Date::operator-(int days) const
{
Date temp(*this);
if (days < 0) {
return temp + (-days);
}
temp._day -= days;
while(temp._day < 1) {
temp._month--;
if (temp._month < 1) {
temp._year--;
temp._month = 12;
}
temp._day += temp.getDayInMonth();
}
return temp;
}
//-=运算符重载
//原来相同,不做过多解释
Date& Date::operator-=(int days)
{
(*this) = (*this) - days;
return *this;
}
//&运算符重载
Date* Date::operator&()
{
return this;
}
//&运算符重载 针对const 对象
const Date* Date::operator&() const
{
return this;
}
//<<运算符重载
//我们都知道,类的成员函数第一个参数为隐含的 this 指针。
//那么对于 << 运算符如果直接重载,重载后,cout输出流就变为了第二个参数,
//而默认的 << 中,cout输出流应该为第一个参数,
//如果使用 cout << a 的形式调用,结果肯定是不对的。
//所以我们在类外进行操作符重载,然后定义为类的友元函数即可
std::ostream& operator<<(std::ostream& _cout, const Date& d)
{
_cout<< d._year<< "-"<< d._month<< "-"<< d._day;
return _cout;
}
//>>运算符重载
std::istream& operator>>(std::istream& _cin, Date& d)
{
_cin>> d._year>> d._month>> d._day;
return _cin;
}
//前置++运算符重载
//只需要对当前对象 + 1 然后返回当前对象即可
Date& Date::operator++()
{
(*this) += 1;
return (*this);
}
//后置++运算符重载
//函数中形参仅仅是用于与前置++构成重载
//后置++ 是 执行完当前语句后才进行++操作,
//所以我们应该先将当前对象保存起来,等当前对象++完成之后,
//再返回之前保存的值,返回值不返回引用的原因为:
//临时变量为栈上元素,函数结束后,会销毁掉
Date Date::operator++(int)
{
Date temp(*this);
(*this) += 1;
return temp;
}
//前置--运算符重载
Date& Date::operator--()
{
(*this) -= 1;
return (*this);
}
//后置++运算符重载
Date Date::operator--(int)
{
Date temp(*this);
(*this) -= 1;
return temp;
}
//-运算符重载
//用两个临时对象maxxdate 和 mindate 分别保存 d 和 当前对象中的 大日期 和 小日期
//写一个循环,让小日期加一个天数 当两个日期相等时,返回加的日期即可。
int Date::operator-(const Date& d) const
{
int ret = 0;
Date mindate;
Date maxdate;
if ((*this) == d) {
return 0;
}
mindate = (*this) < d ? (*this) : d;
maxdate = (*this) > d ? (*this) : d;
while(1) {
if (mindate + ret == maxdate) {
break;
}
++ret;
}
return ret;
}
//>运算符重载
//只需要判断对应成员变量的大小即可
bool Date::operator>(const Date& d) const
{
return (this->_year > d._year)
|| (this->_year == d._year && this->_month > d._month)
|| (this->_year == d._year && this->_month == d._month && this->_day > d._day);
}
//>=运算符重载
bool Date::operator>=(const Date& d) const
{
return (*this) > d || (*this) == d;
}
//>运算符重载
bool Date::operator<(const Date& d) const
{
return (this->_year < d._year)
|| (this->_year == d._year && this->_month < d._month)
|| (this->_year == d._year && this->_month == d._month && this->_day < d._day);
}
//>=运算符重载
bool Date::operator<=(const Date& d) const
{
return (*this) < d || (*this) == d;
}
//==运算符重载
bool Date::operator==(const Date& d) const
{
return this->_year == d._year
&& this->_month == d._month
&& this->_day == d._day;
}
//!=运算符重载
bool Date::operator!=(const Date& d) const
{
return this->_year != d._year
|| this->_month != d._month
|| this->_day != d._day;
}
最后附上代码地址:Mmmmmmi