🌈完整地实现日期类
👻主要使用了构造函数、运算符重载函数。
👻以下日期类包含接收日期、日期与日期减加减、日期与天数间加减、日期自增减(自动加减一天)、日期间大小比较等功能。
☀️一、分析与优化各函数实现
其实没有必要将所有赋值运算符重载都完整地写出来,有很多函数的实现逻辑其实是有大关联的:
对于日期类的赋值运算符重载,可有以下分组:
①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<0while (_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<0while (_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日
}
🎈测试结果
经验证,是正确的。