我们要实现日期的加减、大小比较等功能。
一、声明
这里先创建一个头文件来进行定义声明:
Date(int year = 1900, int month = 1, int day = 1);
void Print();
创建Date对象时初始化日期(年、月、日)
void Print();作为普通成员函数,用于日期打印。
int GetMonthDay(int year, int month){assert(month > 0 && month < 13);static int monthDayArray[13] = { -1, 31, 28, 31, 30, 31, 30,//0月为-1,没有
31, 31, 30, 31, 30, 31 };if (month == 2 && (year % 4 == 0 && year % 100 != 0) || (year% 400 == 0)){return 29;}else{return monthDayArray[month];}return monthDayArray[month];}bool CheckDate();
我们创建一个月份函数GetMonthDay,用于判断每个月固定的天数是多少,并且判断是否为润年。在这因为要频繁调用月份函数,所以建议使用静态数组减少开销。
CheckDate是用于判断,输入的日期是否有误。
//声明定义分离
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+=(int day);
Date operator+(int day);
Date& operator-=(int day);
Date operator-(int day);
Date& operator++();
Date operator++(int);
int operator-(const Date& d);
上面这些就是我们要实现的功能,operator 作为专门用于重载运算符的关键字,我们在这给她先声明。
private:int _year;int _day;int _month;
};
这里就是声明我们的年月日了。
//友元函数声明后可以访问私有函数:
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);
上面是流的重载方便我们进行日期输入和输出,friend 是友元函数的定义,使用友元函数可以访问私有成员函数。
值得注意的是,流的重写不能放在类中,否则会因为内置的隐藏参数this指针导致会以奇怪的方式实现原功能。
下面就是完整的头文件Date.h:
#pragma once
#include
using namespace std;
#include
class Date
{//友元函数声明后可以访问私有函数:friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);
public:Date(int year = 1900, int month = 1, int day = 1);void Print();//获取某一年某一天//GetMonthDay没有声明和定义分离,默认是inline函数,对于这种小而频繁调用的函数设为内联函数减少开销int GetMonthDay(int year, int month){assert(month > 0 && month < 13);static int monthDayArray[13] = { -1, 31, 28, 31, 30, 31, 30,//0月为-1,没有
31, 31, 30, 31, 30, 31 };//因为频繁调用月分,直接放入静态区节省开销//闰年判断if (month == 2 && (year % 4 == 0 && year % 100 != 0) || (year% 400 == 0)){return 29;}else{return monthDayArray[month];}return monthDayArray[month];}bool CheckDate();//判断是否输入错误日期//声明定义分离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+=(int day);Date operator+(int day);Date& operator-=(int day);Date operator-(int day);Date& operator++();Date operator++(int);//这里有++d1和d1++两种形态,所以规定实现++重载的是前置++d1,后置++重载需要添加一个int形参,入d1.operator(0)int operator-(const Date& d);
private://在重载流要使用这三个得设为全局函数,或者增加三个公有函数//或者用另一种方法,友元元声明int _year;int _day;int _month;
};
//流重载
//流重载若放在成员函数里,会不可避免的出现左右操作数要按顺序对齐,导致原本cout<>(istream& in, Date& d);
二、实现
void Date::Print()
{cout << _year << "-" << _month << "-" << _day << endl;
}
函数打印,输出我们输入的日期。
bool Date::CheckDate()
{if (_month < 1 || _month > 12|| _day < 1 || _day > GetMonthDay(_year, _month))//不属于一到十二月,和1到月底日数{return false;}else{return true;}
}
Date::Date(int year, int month, int day)
{_year = year;_month = month;_day = day;if (!CheckDate()) {cout << "非法日期:" << endl;Print();}
}
实现我们的年份判断,当月份和天数不在GetMonthDay规定的对应月份所拥有的天数中和月份不在1-12月时,返回false,并且打印出错误日期。
bool Date:: operator < (const Date& d) {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;}}else {return false ;}}
}
我们开始实现小于比较,通过逐步对比年月日来进行判断。
bool Date:: operator <= (const Date& d) {return *this < d || *this == d;}bool Date:: operator >= (const Date& d) {return *this > d || *this == d;}
bool Date:: operator > (const Date& d) {//return *this < d || *this == d;return !(*this <= d);}
bool Date:: operator == (const Date& d) {return _year == d._year && _month == d._month && _day & d._day;
}
bool Date:: operator != (const Date& d) {return !(*this == d);
}
这里可以使用复用简化代码来实现他们的功能,但要注意的是必须有一个运算符按照<符的方式重写,否则会调入无线调用的死循环里。
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)//如果出现13月{++_year;//进入下一年,月份归为一月_month = 1;}}return *this;
}
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;//进入下一个月// if (tmp._month == 13)//如果出现13月// {// ++tmp._year;//进入下一年,月份归为一月// tmp._month = 1;// }//}return tmp;
}
Date Date::operator-(int day)
{Date tmp = *this;tmp -= day;return tmp;////-=复用-// Date tmp = *this;//if (day < 0)//获取需要减少的天数,入-50天//{// return *this += -day;//}//tmp._day -= day;//当前天数减去需要减去的天数//while (tmp._day <= 0)//剩余天数小于0,退回上一个月,月数不足退回去年的12月,加上回退月数所有的天数,继续减去剩余天数,直到为正。//{// --tmp._month;// if (tmp._month == 0)// {// tmp._month = 12;// tmp._year--;// }// // 借上⼀个⽉的天数// tmp._day += GetMonthDay(tmp._year, tmp._month);// return tmp;一次拷贝//}
}
Date& Date::operator-=(int day)
{if (day < 0)//获取需要减少的天数,入-50天,则转换为正{return *this += -day;}_day -= day;//当前天数减去需要减去的天数while (_day <= 0)//剩余天数小于0,退回上一个月,月数不足退回去年的12月,加上回退月数所有的天数,继续减去剩余天数,直到为正。{--_month;if (_month == 0){_month = 12;_year--;}// 借上⼀个⽉的天数_day += GetMonthDay(_year, _month);}//-=复用-,相比-复用-=这里会多拷贝三次,增加开销.前两次是调用operator-=时两次拷贝和一次赋值拷贝//*this = *this - day;//相当于*this -= day;return *this;
}
这里实现了+、+=、-、-=四个运算符的重载,值得注意的是,在-和-=重写我注释掉了另外一种重写方法,两种方法是谁先重写就先复用谁,但是第二种方法会花费更大的开销。
//d1++ ;括号里数字随意,是整数就行
Date Date::operator++(int) {Date tmp = *this;*this += 1;return tmp;
}
//++d1
Date& Date::operator++() {*this += 1;return *this;
}
然后++的重载有些特殊,因为++分为++d和d++,在C++里规定,d++这种后置写法,重写需要带上参数,否则调用的是++d的重写方法。
//日期相减
int Date::operator-(const Date& d)
{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;
}
这里我们传入两个日期,当max日期小于min日期时进行交换,揉捏在进行循环增加小的日期,直到两日期相等,我们则将重复增加多少次的次数返回,得到的就是两日期相差天数。flag是用于判断起始日期差是增加还是减少。
ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "⽉" << d._day << "⽇" << endl;return out;
}
istream& operator>>(istream& in, Date& d)
{while (1) {cout << "请依次输⼊年⽉⽇:>";in >> d._year >> d._month >> d._day;if (!d.CheckDate()){cout << "⽇期⾮法" << endl;d.Print();cout << "重新输入" << endl;}}return in;
}
这里就是我们重写的输入输出流,方便我们直接输入年月日 并且以更直观的方式输出。
Date.cpp:
#include"Date.h"
bool Date::CheckDate()
{if (_month < 1 || _month > 12|| _day < 1 || _day > GetMonthDay(_year, _month))//不属于一到十二月,和1到月底日数{return false;}else{return true;}
}
Date::Date(int year, int month, int day)
{_year = year;_month = month;_day = day;if (!CheckDate()) {cout << "非法日期:" << endl;Print();}
}
void Date::Print()
{cout << _year << "-" << _month << "-" << _day << endl;
}
bool Date:: operator < (const Date& d) {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;}}else {return false ;}}/*return !(*this>=d);*///这里不用下面方式是因为至少有一个上述比较,否则会出现无限调用·死循环
}
//以此类推
bool Date:: operator <= (const Date& d) {return *this < d || *this == d;}bool Date:: operator >= (const Date& d) {return *this > d || *this == d;}
bool Date:: operator > (const Date& d) {//return *this < d || *this == d;return !(*this <= d);}
bool Date:: operator == (const Date& d) {return _year == d._year && _month == d._month && _day & d._day;//等价上面的判断,这里不用和下面一样的方式是会导致无线递归
}
bool Date:: operator != (const Date& d) {return !(*this == d);
}
//定义
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)//如果出现13月{++_year;//进入下一年,月份归为一月_month = 1;}}return *this;
}
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;//进入下一个月// if (tmp._month == 13)//如果出现13月// {// ++tmp._year;//进入下一年,月份归为一月// tmp._month = 1;// }//}return tmp;
}
Date Date::operator-(int day)
{Date tmp = *this; //一次拷贝tmp -= day;return tmp;//一次拷贝////-=复用-// Date tmp = *this;//if (day < 0)//获取需要减少的天数,入-50天//{// return *this += -day;//}//tmp._day -= day;//当前天数减去需要减去的天数//while (tmp._day <= 0)//剩余天数小于0,退回上一个月,月数不足退回去年的12月,加上回退月数所有的天数,继续减去剩余天数,直到为正。//{// --tmp._month;// if (tmp._month == 0)// {// tmp._month = 12;// tmp._year--;// }// // 借上⼀个⽉的天数// tmp._day += GetMonthDay(tmp._year, tmp._month);// return tmp;一次拷贝//}
}
Date& Date::operator-=(int day)
{if (day < 0)//获取需要减少的天数,入-50天,则转换为正{return *this += -day;}_day -= day;//当前天数减去需要减去的天数while (_day <= 0)//剩余天数小于0,退回上一个月,月数不足退回去年的12月,加上回退月数所有的天数,继续减去剩余天数,直到为正。{--_month;if (_month == 0){_month = 12;_year--;}// 借上⼀个⽉的天数_day += GetMonthDay(_year, _month);}//-=复用-,相比-复用-=这里会多拷贝三次,增加开销.前两次是调用operator-=时两次拷贝和一次赋值拷贝//*this = *this - day;//相当于*this -= day;return *this;
}//d1++ ;括号里数字随意,是整数就行Date Date::operator++(int) {Date tmp = *this;*this += 1;return tmp;}//++d1Date& Date::operator++() {*this += 1;return *this;}//前置后置推荐使用前置,后置多两次拷贝//日期相减int Date::operator-(const Date& d){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;}ostream& operator<<(ostream& out, const Date& d){out << d._year << "年" << d._month << "⽉" << d._day << "⽇" << endl;return out;}istream& operator>>(istream& in, Date& d){while (1) {cout << "请依次输⼊年⽉⽇:>";in >> d._year >> d._month >> d._day;if (!d.CheckDate()){cout << "⽇期⾮法" << endl;d.Print();cout << "重新输入" << endl;}}return in;}