我们要实现日期的加减、大小比较等功能。
一、声明
这里先创建一个头文件来进行定义声明:
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;
}
//++d1
Date& 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;
}
浙公网安备 33010602011771号