🌈完整地实现日期类
👻主要使用了构造函数、运算符重载函数。
👻以下日期类包含接收日期、日期与日期减加减、日期与天数间加减、日期自增减(自动加减一天)、日期间大小比较等功能。
☀️一、分析与优化各函数实现
其实没有必要将所有赋值运算符重载都完整地写出来,有很多函数的实现逻辑其实是有大关联的:
对于日期类的赋值运算符重载,可有以下分组:
①operator== & operator!=;二者可逻辑取反。
②operator+ & operator+= ;一个在另一个基础上。
③operator- & operator-=;一个在另一个基础上。
④operator++ & operator++(int) & operator-- & operator–(int);后置的比前置的多了个整型参数
⑤operator> & operator<= & operator< & operator>=;可以多次逻辑取反。
🎈1.operator== & operator!=
先写==,!=即为==的逻辑取反
//判断是否相等
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);
}
🎈2.operator> & operator<= & operator< & operator>=
最开始只用写一个函数即可,比如>,剩下的函数都可以复用这个函数了,即:>函数的取反是<=函数;>的结果并上==的结果就得到>=函数;>=函数的取反是<函数。
//判断是否大于
bool Date::operator>(const Date& d) {
if (_year > d._year)
return true;
else if ((_year == d._year) && (_month > d._month))
return true;
else if ((_year == d._year) && (_month == d._month)) {
if (_day > d._day)
return true;
}
return false;
}
//判断是否小于等于
bool Date::operator<=(const Date& d) {
return !(*this > d);
}
//判断是否大于等于
bool Date::operator>=(const Date& d) {
return *this == d || *this > d;
}
//判断是否小于
bool Date::operator<(const Date& d) {
return !(*this >= d);
}
🎈3.operator+ & operator+=
可以先写+函数,在+函数的基础上复用+函数得到+=函数;也可以先写+=再写+,到底选择哪一种方式呢?选拷贝构造次数少的方式,如下方分析:
🌟(1)先写+=函数,复用+=函数得到+函数
//日期+=天数
Date& Date::operator+=(int x) {
if(x<0){
return *this-=x;
}
_day += x;
while (_day > GetMonthDay(_year,_month)) {
_day -= GetMonthDay(_year,_month);
_month++;
if (_month > 12) {
_month = 1;
_year++;
}
}
return *this;
}
//(在+=函数的基础上实现)日期+天数
Date Date::operator+(int x) {
Date tmpd(*this);
tmpd += x;
return tmpd;
}
+=函数中拷贝构造对象次数:0(未涉及拷贝构造对象,且传引用返回,一共拷贝构造了0次)
+=函数中拷贝构造对象次数:2(+在+=的基础上,因此先拷贝复制了0次;之后又拷贝构造了tmpd对象,返回tmpd时用的传值返回,一共拷贝构造了2次)
🌟(2)先写+函数,复用+函数得到+=函数
//日期+天数
Date Date::operator+(int x) {
if(x<0){
return *this-x;
}
Date tmpd(*this);
tmpd._day += x;
while(tmpd._day > GetMonthDay(tmpd._month)) {
tmpd._day -= GetMonthDay(tmpd._month);
tmpd._month++;
if (tmpd._month > 12) {
tmpd._month = 1;
tmpd._year++;
}
}
return tmpd;
}
//(在+函数的基础上实现)日期+=天数
Date& Date::operator+=(int x) {
*this = *this + x;
return *this;
}
+函数中拷贝构造对象次数:2(用this指针拷贝构造对象、传值返回了类对象,一共拷贝构造了2次)
+=函数中拷贝构造对象次数:3(由于+=在+的基础上,因此先拷贝复制了2次;然后又给this对象赋值了一次,一共拷贝构造了3次)
确定谁复用谁的意义:对于日期类对象,二者拷贝对象次数的差异可能影响不大,但是换一个庞大的类类型,拷贝构造动作会用掉很多空间,会降低效率,因此会产生巨大差异
👻最优方式:先写+=函数,再复用+=函数得到+函数。尽量少地进行拷贝构造动作
🎈4.operator- & operator-=
与+=和+函数同理,最优方式是先写-=函数,再复用-=函数得到-函数。
//日期-=天数=新日期(本身)
Date& Date::operator-=(int x) {
if(x<0){
return *this+=x;
}
_day -= x;
if (_day > 0) {
return *this;
}
else if (_day == 0) {
_month--;
if (_month == 0) {
_month = 12;
_year--;
}
_day = GetMonthDay(_year,_month);
return *this;
}
else { //tmp._day<0
while (_day < 0) {
_month--;
_day = GetMonthDay(_year,_month) + _day;
if (_day == 0) {
_month--;
if (_month == 0) {
_month = 12;
_year--;
}
_day = GetMonthDay(_year,_month);
}
}
return *this;
}
}
//日期-天数=日期(拷贝)
Date Date::operator-(int x) {
Date tmp(*this);
tmp -= x;
return tmp;
}
🎈5.一个特殊的减法重载:日期-日期=天数
对于日期而言,加法这个运算操作一般用于在日期的基础上加天数,得到推算出的日期,很少有两个日期相加的运算;
而减法可以用于日期间天数,得到倒退的日期,还可以日期减日期,得到二者相差多少天
问:是“否日期-日期=天数”的这个重载和operator-重载冲突?
答:不会,因为二者的参数一个是Date类日期对象,另一个是整型数值,返回值一个是整型数值,另一个是Date类日期对象。编译器可以根据函数调用匹配到正确的函数重载。
相减思路:
现有两个日期,需要算出二者间相隔多少天。
1.比较出两个日期中较大的那一个,大的赋值给max对象,小的赋值给min对象;
2.算出大日期是那一年的第几天,记为pass变量;
3.算出小日期距离那一年末还有多少天,记为to变量;
4.得出两日期间相差多少天(算出两日期间完整的年份的天数和,加pass,再加to)
//日期-日期=天数
int Date::operator-(const Date& d) {
//1.比较出两个日期中较大的一个
Date max(1, 1, 1);
Date min(1, 1, 1);
if (*this > d) {
max = *this;
min = d;
}
else if (*this == d)
return 0;
else {
max = d;
min = *this;
}
//2.算出大日期是那一年的第几天
int pass = 0;
pass += max._day;
for (int i = 1;i < max._month;i++) {
pass += GetMonthDay(max._year,i);
}
//3.算出小日期距离那一年末还有多少天
int yearDay = GetYearDay(min._year);
int now = 0;
for (int i = 1;i < min._month;i++) {
now += GetMonthDay(min._year,i);
}
now += min._day;
int to = yearDay - now;
//4.得出两日期间相差多少天
min._year++;
if (min._year > max._year) {
int tmp = GetYearDay(max._year);
return pass + to - tmp;
}
int tmp = 0;
for (int i = min._year;i < max._year;i++) {
tmp += GetYearDay(i);
}
return pass + to + tmp;
}
🎈6.operator++ & operator++(int) & operator-- & operator–(int)
前置++和后置++的区别(–同理):
(1)前置++表示先++后使用,即变量内的值马上+1,后续使用得到的都是+1后的值;
(2)后置++表示先使用后++,即先使用变量原来的值,使用完后马上+1;
(3)前置++效率更高,因为后置++还需要多一次拷贝。对于内置类型二者差异不大,但对于自定义类型最好用前置,减少拷贝的次数
规定:为了和++d1区分,d1++无参数,而d1++的函数,参数必须为int。
//前置++(日期自动+1天)
Date& Date::operator++() {
*this += 1;
return *this;
}
//后置++(日期自动+1天)
Date Date::operator++(int) {
Date tmp(*this);
*this += 1;
return tmp;
}
//前置--(日期自动-1天)
Date& Date::operator--() {
*this -= 1;
return *this;
}
//后置--(日期自动-1天)
Date Date::operator--(int) {
Date tmp(*this);
*this -= 1;
return tmp;
}
☀️二、完整Date类实现和Test、main函数
注意:
1.析构和拷贝构造函数对于日期类而言不用写。
2.分文件时,.cpp文件中存放的函数实现需要指定类域。
3.对于缺省参数,为了防止声明与定义的参数默认值不一致,只能在声明时给出。
🎈1.Date.h头文件
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
class Date {
private:
int _year;
int _month;
int _day;
public:
//构造函数
Date(int year, int month, int day);
//析构函数、拷贝构造函数不用显式写
//得到某月的总天数
int GetMonthDay(int year, int month)const;
//得到某年的总天数
int GetYearDay(int year)const;
//打印日期
void Print()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;
//日期+天数
/*Date operator+(int x);
//(在+函数的基础上实现)日期+=天数
/*Date& operator+=(int x);*/
//日期+=天数
Date& operator+=(int x);
//(在+=函数的基础上实现)日期+天数
Date operator+(int x)const;
//日期-日期=天数
int operator-(const Date& d)const;
//日期-=天数=新日期(本身)
Date& operator-=(int x);
//日期-天数=日期(拷贝)
Date operator-(int x)const;
//前置++(日期自动+1天)
Date& operator++();
//后置++(日期自动+1天)
Date operator++(int);
//前置--(日期自动-1天)
Date& operator--();
//后置--(日期自动-1天)
Date operator--(int);
};
🎈2.Date.cpp函数定义
#include"Date.h"
//构造函数
Date::Date(int year, int month, int day) {
_year = year;
_month = month;
_day = day;
//同时完善日期检查功能
if (_year < 1 ||
_month < 1 || _month > 12 ||
_day < 1 || _day > GetMonthDay(_year,_month)){
cout << "日期错误" << endl;
return;
}
}
//得到某月的总天数
int Date::GetMonthDay(int year,int month)const {
int arr[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
arr[2] = 29;
return arr[month];
}
//得到某年的总天数
int Date::GetYearDay(int year) const {
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
return 366;
return 365;
}
//打印日期
void Date::Print() const {
cout << _year << "年" << _month << "月" << _day << "日" << endl;
}
//判断是否相等
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);
}
//判断是否大于
bool Date::operator>(const Date& d) const {
if (_year > d._year)
return true;
else if ((_year == d._year) && (_month > d._month))
return true;
else if ((_year == d._year) && (_month == d._month)) {
if (_day > d._day)
return true;
}
return false;
}
//判断是否小于等于
bool Date::operator<=(const Date& d) const {
return !(*this > d);
}
//判断是否大于等于
bool Date::operator>=(const Date& d) const {
return *this == d || *this > d;
}
//判断是否小于
bool Date::operator<(const Date& d) const {
return !(*this >= d);
}
//日期+=天数
Date& Date::operator+=(int x) {
_day += x;
while (_day > GetMonthDay(_year,_month)) {
_day -= GetMonthDay(_year,_month);
_month++;
if (_month > 12) {
_month = 1;
_year++;
}
}
return *this;
}
//(在+=函数的基础上实现)日期+天数
Date Date::operator+(int x) const {
Date tmpd(*this);
tmpd += x;
return tmpd;
}
//日期-日期=天数
int Date::operator-(const Date& d) const {
//1.比较出两个日期中较大的一个
Date max(1, 1, 1);
Date min(1, 1, 1);
if (*this > d) {
max = *this;
min = d;
}
else if (*this == d)
return 0;
else {
max = d;
min = *this;
}
//2.算出大日期是哪一月的第几天
int pass = 0;
pass += max._day;
for (int i = 1;i < max._month;i++) {
pass += GetMonthDay(max._year,i);
}
//3.算出小日期距离那一年末还有多少天
int yearDay = GetYearDay(min._year);
int now = 0;
for (int i = 1;i < min._month;i++) {
now += GetMonthDay(min._year,i);
}
now += min._day;
int to = yearDay - now;
//4.得出两日期间相差多少天
min._year++;
if (min._year > max._year) {
int tmp = GetYearDay(max._year);
return pass + to - tmp;
}
int tmp = 0;
for (int i = min._year;i < max._year;i++) {
tmp += GetYearDay(i);
}
return pass + to + tmp;
}
//日期-=天数=新日期(本身)
Date& Date::operator-=(int x) {
_day -= x;
if (_day > 0) {
return *this;
}
else if (_day == 0) {
_month--;
if (_month == 0) {
_month = 12;
_year--;
}
_day = GetMonthDay(_year,_month);
return *this;
}
else { //tmp._day<0
while (_day < 0) {
_month--;
_day = GetMonthDay(_year,_month) + _day;
if (_day == 0) {
_month--;
if (_month == 0) {
_month = 12;
_year--;
}
_day = GetMonthDay(_year,_month);
}
}
return *this;
}
}
//日期-天数=日期(拷贝)
Date Date::operator-(int x) const {
Date tmp(*this);
tmp -= x;
return tmp;
}
//前置++(日期自动+1天)
Date& Date::operator++() {
*this += 1;
return *this;
}
//后置++(日期自动+1天)
Date Date::operator++(int) {
Date tmp(*this);
*this += 1;
return tmp;
}
//前置--(日期自动-1天)
Date& Date::operator--() {
*this -= 1;
return *this;
}
//后置--(日期自动-1天)
Date Date::operator--(int) {
Date tmp(*this);
*this -= 1;
return tmp;
}
🎈3.Test.cpp测试
#include"Date.h"
int main() {
Date d1(2024, 1, 31);
Date d2(2000, 2, 12);
Date d3(d1);
int flag = 0;
//测试==和!=
if (d1 == d2)
flag = 1;
if (d1 != d2)
flag = 2;
cout << flag << endl; //2
//测试>、<
if (d1 > d2)
flag = 3;
if (d1 <= d2)
flag = 4;
cout << flag << endl; //3
//测试>=、<=
if (d1 >= d3)
flag = 5;
if (d1 < d3)
flag = 6;
cout << flag << endl; //5
//计算+、+=
Date a = d2 + 5;
a.Print(); //2000年2月17日
Date b = d2 + 55;
b.Print(); //2000年4月7日
d2.Print(); //2000年2月12日
d2 += 5;
d2.Print(); //2020年2月17日
d2 += 55;
d2.Print(); //2020年4月12日
//计算-、-=
Date c = d2 - 55;
c.Print(); //2000年2月17日
Date d = d2 - 5;
d.Print(); //2000年4月7日
d2.Print(); //2020年4月12日
d2 -= 55;
d2.Print(); //2000年2月17日
d2 -= 5;
d2.Print(); //2000年2月12日
//计算两日期相隔多少天
int x = d1 - d2;
cout << x << endl; //8754
//测试前置++、后置++
Date tmp1(d2++);
tmp1.Print(); //2000年2月12日
d2.Print(); //2000年2月13日
Date tmp2(++d2);
tmp2.Print(); //2000年2月14日
d2.Print(); //2000年2月14日
//测试前置--、后置--
Date tmp3(d2--);
tmp3.Print(); //2000年2月14日
d2.Print(); //2000年2月13日
Date tmp4(--d2);
tmp4.Print(); //2000年2月12日
d2.Print(); //2000年2月12日
}
🎈测试结果
经验证,是正确的。