目录
- 日期类的实现
- 比较功能的实现
- 日期类的构造函数
- Date::Date(int year,int month,int day)代码
- 判断日期大小
- 判断日期类d1是否小于d2
- bool Date::operator<(const Date& d)代码
- 判断日期类d1是否小于等于d2
- bool Date::operator<=(const Date& d) 代码
- 判断日期类d1是否大于d2
- bool Date::operator>(const Date& d)代码
- 判断日期类d1是否大于等于d2
- bool Date::operator>=(const Date& d)代码
- 判断日期类d1是否等于d2
- bool Date::operator==(const Date& d)代码
- bool Date::operator!=(const Date& d)代码
- 比较函数实现总结
- 日期类的加减乘除
- 日期加天数的实现
- int GetMonthDay(int year, int month)代码
- 日期加写法一
- 代码Date Date:: operator+(int day )
- 日期加写法二
- Date& Date:: operator+=(int day)代码
- Date& Date:: operator+(int day)代码
- 拓展
- 补充
- 日期减天数的实现
- Date& Date::operator-=(int day)代码
- Date Date::operator-(int day)代码
- 日期减日期的实现
感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接
🐒🐒🐒 个人主页
🥸🥸🥸 C语言
🐿️🐿️🐿️ C语言例题
🐣🐣🐣 python
🐓🐓🐓 数据结构C语言
🐔🐔🐔 C++
🐿️🐿️🐿️ 文章链接目录
日期类的实现
//Date.h文件
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1);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);void Print(){cout << _year << "/" << _month << "/" << _day << endl;}
private:int _year;int _month;int _day;
};
比较功能的实现
日期类的构造函数
Date::Date(int year,int month,int day)代码
//Date.cpp文件
Date::Date(int year,int month,int day)
{_year = year;_month = month;_day = day;
}
判断日期大小
判断日期类d1是否小于d2
bool Date::operator<(const Date& d)代码
//Date.cpp文件
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){return _day < d._day;}}return false;
}
判断日期类d1是否小于等于d2
bool Date::operator<=(const Date& d) 代码
//Date.cpp文件
bool Date::operator<=(const Date& d)
{return *this < d || *this == d;
}
判断日期类d1是否大于d2
bool Date::operator>(const Date& d)代码
//Date.cpp文件
bool Date::operator>(const Date& d)
{return !(*this <= d);
}
判断日期类d1是否大于等于d2
bool Date::operator>=(const Date& d)代码
//Date.cpp文件
bool Date::operator>=(const Date& d)
{return !(*this < d);
}
判断日期类d1是否等于d2
bool Date::operator==(const Date& d)代码
//Date.cpp文件
bool Date::operator==(const Date& d)
{return _year == d._year&& _month == d._month&& _day == d._day;
}
bool Date::operator!=(const Date& d)代码
bool Date::operator!=(const Date& d)
{return !(*this == d);
}
比较函数实现总结
通过上面的代码我们可以看出函数是可以互用的,并且代码中灵活的利用this指针与引用d进行比较,得出最后的是否为真
日期类的加减乘除
两个日期类相加在现实中并没有意义,而一个日期类加一个天数,得到的新的日期类,以及两个日期类相减,在现实中有很大意义
在实现这些功能之前我们需要理解运算符重载中的一句话:重载操作符必须有一个类类型参数,这里的必须表示最少有一个类就可以,说明重载操作符是可以支持其他类型的参数的
日期加天数的实现
对于日期类的加减天数需要注意的问题就是进位
我们知道每个月的天数不是一样的,比如1月和4月,1月是31天,而4月是30天,在加减天数的时候需要将所在月份的天数考虑进去,特别要注意2月,因为闰年的2月和平年的2月天数是不一样的,所以还需要判断是闰年还是平年
我们先看看进位的思想,比如2024/5/11加5天是不用进位的,因为16小于31
而当我们继续加上20天后,原来的16就变成了36,36大于31,所以需要进位
进位后需要将原来的36减去31,同时让5变到6
有了这个思路,接下来就是具体实现了
再实现功能前我们需要在Date的头文件中创建一个GetMonthDay函数,目的是调用函数的时候可以获得当月的天数
因为除了2月其他的月份天数都是固定的,所以我们可以用一个数组monthDays去存储每个月份的天数
这里有一个细节,我们让monthDays[0]=0,因为月份是从1开始的,所以在访问数组的时候,我们只需要从下标为1开始访问就可以了,下标为0就当他不存在
int GetMonthDay(int year, int month)
{assert(month > 0 && month < 13);int monthDays[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)&&month==2){return 29;}return monthDays[month];
}
这段代码是可以进一步优化的
第一个优化是GetMonthDay是一个函数,并且在这个日期类中使用是非常频繁的
我们都知道函数的调用会开辟栈帧,函数频繁调用,每次都要创建数组 monthDays,而开辟的这些空间里装的都是同一个数据,这不是我们想看到的结果
所以用static修饰 monthDays(因为静态区只会创建出一个数组)
第二个优化是if语句(虽然这个优化可能没什么作用),将month==2和前面的条件判断交换位置
if ( month == 2&&(year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
最后GetMonthDay代码如下,但是要注意GetMonthDay要放在公有的区域里,如果放在私有就访问不了了
int GetMonthDay(int year, int month)代码
//Date.h
int GetMonthDay(int year, int month)
{assert(month > 0 && month < 13);static int monthDays[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };if ( month == 2&&(year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)){return 29;}return monthDays[month];
}
接下来就是实现日期相加的功能
根据上面的逻辑实现的代码如下
日期加写法一
Date& Date:: operator+(int day )
{_day += day;while (_day > GetMonthDay(_year, _month))//判断是否大于当前月的天数{_day -= GetMonthDay(_year, _month);//让日期减去当月的天数++_month;if (_month == 13)//month=13就代表应该要让年进一位了{++ _year;_month = 1;//让month回到1}}return *this;//返回修改后的类
}
我们测试一下
测试后看起来的确是没有问题的,但是我们再来看看下面这个情况
当我们调用d1.Print()后发现d1被修改了
问题其实是在d2=d1+21上,d1+21会调用operator+(int day )
而这个函数中的_day += day,++_month,++ _year,都是改变的d1,然后将修改后的d1作为返回值(注意这里还是会被临时变量拷贝),d2会拷贝临时变量,最后导致d1和d2都被修改
所以我们上面实现的函数其实是operator+=(int day ),而不是operator+(int day )
那如何去实现operator+(int day )呢?
我们可以通过拷贝构造,将d1拷贝一份给tmp,然后后面的过程都不变,后面这样还是会改变tmp,但是tmp只是一个局部变量,出来函数作用域后就不存在了,所以没有影响,但是要注意tmp是局部变量,所以返回类型不能用引用
代码Date Date:: operator+(int day )
//Date.cpp文件
Date Date:: operator+(int day )
{Date tmp(*this);tmp._day += day;//tmp拷贝后,后面的_day...都是tmp的成员变量while (tmp._day > GetMonthDay(_year, _month))//大于当前月的天数{tmp._day -= GetMonthDay(tmp._year, tmp._month);++tmp._month;if (tmp._month == 13){++tmp._year;tmp._month = 1;}}return tmp;//因为tmo拷贝的是*this,所以只需要返回tmp,而不是*tmp
}
日期加写法二
写法一中在实现operator+(int day )的时候我们意外实现出了operator+=(int day ),从代码的逻辑上看这两个函数几乎都是相同的,那可不可以互用呢?
Date& Date:: operator+=(int day)代码
Date& Date:: operator+=(int 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)代码
Date Date:: operator+(int day)
{Date tmp=*this;//或者Date tmp(*this);tmp+= day;return tmp;
}
我们只需要对 operator+(int day)稍微改一下就可以了
对比一下两个写法,我们可以发现红色方框和绿色方框的实现是几乎一样的,唯独深蓝色方框中的tmp+=day是额外加上去的
这是因为tmp+=day可以调用 operator+=(int day),也就是operator+(int day)函数实现的内部互用了operator+=(int day),这个实现的逻辑和之前判断日期大小方式相同
拓展
既然实现了operator+=(int day)可以让operator+(int day)互用,那实现了operator+(int day)是否也可以让operator+=(int day)互用呢
Date Date:: operator+(int day)
{Date tmp(*this);tmp._day += day;while (tmp._day > GetMonthDay(_year, _month))//大于当前月的天数{tmp._day -= GetMonthDay(tmp._year, tmp._month);++tmp._month;if (tmp._month == 13){++tmp._year;tmp._month = 1;}}return tmp;
}
Date&Date::operator+=(int day)
{*this=*this+day;return *this;
}
因为operator+=(int day)要让this也要改变,而this+day虽然互用了operator+(int day),但是operator+(int day)是通过拷贝构造出一个tmp去修改的,函数内部并没有改变this指针,所以要解决这个问题我们只需要写成*this=*this+day就可以解决了
operator+(int day)互用operator+=(int day)和operator+=(int day)互用operator+(int day)都实现完后,他们的实现方法是否都一样呢?
可以看到一个是引用返回,另一个不是,这其实是因为我自己忘记加上了🤣🤣,并不是不可以加引用符号,因为返回的都是this指针,并不是临时变量tmp,对于返回的是tmp的函数不可以加引用符号,因为出了作用域会被销毁
那加上后请问哪个的实现方法更好呢?
在对比的时候我们要+对比+,+=对比+=,
先对比+的实现,+的实现对比下来开辟的空间都是一样的
再对比+=的实现
所以最终右边的实现方法更好一点,因为少开了一点空间
补充
Date tmp(*this)可不可以改成Date tmp=*this,改之后是拷贝构造还是赋值呢
我们需要再一次理解一下拷贝构造和赋值
拷贝构造:同类型的一个存在对象进行初始化要创建的对象
拷贝赋值:已经存在的对象,一个拷贝赋值给另一个
Date tmp=this中的tmp并不是一个已经存在的对象,而是用一个已经存在的对象this去初始话另一个正在构造的对象tmp,所以这虽然是用的赋值符号=,但是从定义来看这是一个拷贝构造
同样的Date tmp(*this)也是拷贝构造
日期减天数的实现
根据上面代码实现的对比,日期类减天数的实现我们应该优先实现operator-=(int day),再实现operator-(int day)
这个函数的实现思想主要是借位
当_day<=0的时候就需要让_month减1,然后给_day加上_month减去1后对应的天数,当_month==0的时候就需要让_year减1,让_month=12
Date& Date::operator-=(int day)代码
//Date.cpp文件
Date& Date::operator-=(int day)
{_day -= day;while (_day <= 0){--_month;if (_month == 0){--_year;_month = 12;}_day += GetMonthDay(_year,_month);}return *this;
}
这段代码中的–_month,和条件判断中的(_month == 0)有效的解决了当减去非常多的天数时,也就是month变成<0的情况
operator-(int day)的实现就和operator+(int day)一样了
Date Date::operator-(int day)代码
//Date.cpp文件
Date Date::operator-(int day)
{Date tmp = *this;tmp -= day;return tmp;
}
我们再减一些比较大的值,这些结果可以在网上的日期计算器里去验证
再来验证一下+的实现
日期减日期的实现
日期减日期非常麻烦,我们都知道闰年和平年是相差1天的,在相减时年回不断的变化,并且在减的过程当中可能会出现多个闰年和平年,这样减会非常麻烦
那有什么办法可以简化的吗?
看看下面这个代码
int Date::operator-(const Date& d)
{int flag = 1;Date max = *this;Date min = d;if (*this < d){int flag = -1;max = d;min = *this;}int n = 0;while (min != max){++min;++n;}return n * flag;
}
我们在函数中创建了两个对象max和min,max=*this,min=d,以及一个整形flag=1(这个flag用处很大)
但是对于this和d我们并不知道谁大谁小,所以在执行函数的过程当中需要加一个条件判断,如果this<d就让两个对象交换,并且让flag=-1
之后定义一个n,用于计算出min和max之间相差多少天
最后返回的是nflag,因为不确定this和d的大小,所以需要借助flag确定最后减出的值是正还是负
这个函数是互用了前面的前置++(前置++在(运算符重载(下))中实现的),以及Date Date::operator-(int day),所以要想实现这个函数,前面的函数也必须得实现出来
此外这个函数中有一个细节就是while的条件判断中并没有用min<max来作为条件判断,因为小于的比较逻辑更复杂,需要将年月日都进行比较,所以选择用min!=max