《详解 C++ Date 类的设计与实现:从运算符重载到功能测试》
🔥个人主页:@草莓熊Lotso
🎬作者简介:C++研发方向学习者
📖个人专栏: 《C语言》 《数据结构与算法》《C语言刷题集》《Leetcode刷题指南》
⭐️人生格言:生活是默默的坚持,毅力是永久的享受。
前言: 这篇博客主要会介绍一下Date类的实现,需要运用到前面学习的一些C++的知识。同时,也可以通过这个小练习来检验一下自己的学习成果,我会先把.h文件放在前言后面,大家可以自己先去实现一下试试,再来看看博主的文章。
Date.h:
#pragma onceclass Date
{
public:// 获取某年某月的天数int GetMonthDay(int year, int month);// 全缺省的构造函数Date(int year = 1900, int month = 1, int day = 1);// 拷贝构造函数// d2(d1)--this//Date(const Date& d);// 赋值运算符重载--其实也可以不写// d2 = d3 -> d2.operator=(&d2, d3)Date& operator=(const Date& d);// 析构函数/*~Date();*/// 日期+=天数Date& operator+=(int day);// 日期+天数Date operator+(int day);// 日期-天数Date operator-(int day);// 日期-=天数Date& operator-=(int day);// 前置++Date& operator++();// 后置++Date operator++(int);// 后置--Date operator--(int);// 前置--Date& operator--();// >运算符重载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);// 日期-日期 返回天数int operator-(const Date& d);//打印void Print();private:int _year;int _month;int _day;
};
注意:博客中像打印,析构,拷贝,构造,赋值运算符重载这些之前实现过很多次的就不会再出现了,大家可以自己去实现一下(当然Date类的析构,拷贝,赋值运算符重载都是可以不写的),最后也可以直接在代码总结里面看哈 。
目录
一.Date类+=和+的运算符重载实现与复用
1.1 +=
1.2 +
1.3 复用
+复用+=:
+=复用+:
对比图:编辑
test.cpp:
二.Date类-和-=的运算符重载实现与复用
2.1 -=
2.2 -
test.cpp:
三.Date类前置++,--与后置++,--的运算符重载实现
3.1 前置++
3.2 后置++
test.cpp:
3.3 前置--
3.4 后置--
test.cpp:
四.Date类比较符号的运算符重载实现
4.1 ==
4.2 >
4.3 >=
4.4 <
4.5 <=
4.6 !=
test.cpp:
五.Date类日期-日期的重载实现
日期-日期
test.cpp:
六.代码总览
Date.h:
Date.cpp:
test.cpp:
一.Date类+=和+的运算符重载实现与复用
1.1 +=
--我们在实现日期+=天数之间,还是需要先实现一个获取每月天数的函数的,这个可以直接在.h里面写,比较简单,这里就直接展示给大家看了。
// 获取某年某月的天数
int GetMonthDay(int year, int month)
{assert(month > 0 && month < 13);static int MonthDayArray[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;}else {return MonthDayArray[month];}
}
在有了这个函数之后,我们就可以直接实现+=的重载了,注意分文件写的时候.cpp里面需要带上类域
//日期+=天数
Date& Date::operator+=(int day)
{_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);++_month;if (_month == 13){++_year;_month = 1;}}//this出了作用域还在,所以可以用引用返回,减少拷贝return *this;
}
思路:
--因为是+=所以原来的也会改变,不需要先拷贝一份。我们直接让当前日期的天数加上给的天数,然后进入循环,直到日期合规为止。在循环里每次减掉当月的天数,再++month。当月份达到13时就加年然后把月置为1。就这样,当循环结束时,返回*this(可以使用引用返回)
1.2 +
--有了+=之后实现+也是很简单的,我们先来看看代码吧
// 日期+天数
Date Date::operator+(int day)
{//加天数需要原来的不改变Date tmp(*this);//剩余逻辑和+=类似tmp._day += day;while (tmp._day > GetMonthDay(tmp._year, tmp._month)){tmp._day -= GetMonthDay(tmp._year, tmp._month);++tmp._month;if (tmp._month == 13){++tmp._year;tmp._month = 1;}}return tmp;
}
思路:
--我们直接多加一个对象tmp先把*this拷贝给它,用它去+就行了,最后返回这个tmp就行(注意tmp出了作用域会销毁所以不能使用传引用返回)
1.3 复用
--写完之后会发现上面的代码重复度很高,那我们是不是可以直接复用呢?那是+复用+=好还是+=复用+好呢,我们接着往下看吧
+复用+=:
//+复用+=
Date Date::operator+(int day)
{Date tmp(*this);tmp += day;return tmp;
}
+=复用+:
//+=复用+
Date& Date::operator+=(int day)
{*this = *this + day;return *this;
}
对比图:
--最后我们可以得出用拷贝多的复用拷贝少的更好,也就是+复用+= ,那么后续的-和-=也是一样的道理,-复用-=会更好。
test.cpp:
#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"void test()
{// 日期+=天数Date d1(2025, 8, 3);Date d2=d1+=100;//d1.operator+=(100);d1.Print();d2.Print();// 日期+天数Date d3(2025, 8, 3);Date d4= d3 + 100;//d3.operator+(100);d3.Print();d4.Print();
}int main()
{test();return 0;
}
--测试完成,打印出来的结果都符合预期,退出码为0
二.Date类-和-=的运算符重载实现与复用
2.1 -=
--我们先来看下-=实现的代码,再来了解思路
// 日期-=天数
Date& Date::operator-=(int day)
{_day -= day;while (_day <= 0){_month--;if (_month == 0){_month = 12;--_year;}_day += GetMonthDay(_year, _month);}return *this;
}
思路:
-=和+=一样,原来的也会改变所以不需要额外的对象,然后最后也是直接返回*this就行,可以使用引用返回减少了拷贝。中间的实现思路就是先用当前日期天数减去给的天数,如果<=0就进入循环,先--month(如果为0了就令它变为12之后--year),然后再用天数+=当前月份天数。直到循环结束,返回*this。
2.2 -
--有了上面的经验之后,我们直接选择复用-=就行了
// 日期-天数
//直接复用
Date Date::operator-(int day)
{//-的话原来的不改变Date tmp(*this);tmp -= day;return tmp;
}
思路:
依旧是利用了一个对象tmp先拷贝*this,再直接用tmp-=并返回就行了
test.cpp:
#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"void test()
{// 日期-=天数Date d1(2025, 8, 3);Date d2 = d1 -= 100;//d1.operator-=(100);d1.Print();d2.Print();// 日期-天数Date d3(2025, 8, 3);Date d4 = d3 - 100;//d3.operator-=(100);d3.Print();d4.Print();
}int main()
{test();return 0;
}
--测试完成,打印出来的结果都符合预期,退出码为0
三.Date类前置++,--与后置++,--的运算符重载实现
--关于前置和后置的区别,我们在C语言中就讲过,前置的是先运算后赋值,后置是先赋值后运算。所以它们的区别主要在于需要返回值的时候。那么在运算符重载中为了区分前置和后置,我们选择了给后置加上一个形参,这个形参甚至可以什么都不给,这也是上一篇中没讲的一个运算符成重载的特点
3.1 前置++
--有了前面的一些实现这里直接复用就行了,很简单,直接看注释和代码就行
// 前置++
Date& Date::operator++()
{//前置++先++后赋值,所以返回值是运算之后的*this += 1;return *this;
}
3.2 后置++
--有了前面的一些实现这里直接复用就行了,很简单,直接看注释和代码就行
// 后置++
Date Date::operator++(int)
{//后置++先赋值后++,所以返回值是运算之前的。//我们先把原来的保存下来Date tmp(*this);*this += 1;return tmp;
}
test.cpp:
#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"void test()
{// 前置++Date d1(2025, 8, 3);Date ret1 = ++d1;d1.Print();//2025,8,4ret1.Print();//2025,8,4//后置++Date d2(2025, 8, 3);Date ret2 = d2++;d1.Print();//2025,8,4ret2.Print();//2025,8,3
}int main()
{test();return 0;
}
--测试完成,打印结果符合预期,退出码为0
3.3 前置--
--有了前面的一些实现这里直接复用就行了,很简单,直接看注释和代码就行
// 前置--
Date& Date::operator--()
{//前置--先--后赋值,所以返回值是运算之后的*this -= 1;return *this;
}
3.4 后置--
--有了前面的一些实现这里直接复用就行了,很简单,直接看注释和代码就行
// 后置--
Date Date::operator--(int)
{//后置--先赋值后--,所以返回值是运算之前的。//我们先把原来的保存下来Date tmp(*this);*this -= 1;return tmp;
}
test.cpp:
#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"void test()
{//前置--Date d1(2025, 8, 3);Date ret1=--d1;d1.Print();//2025,8,2ret1.Print();//2025,8,2//后置--Date d2(2025, 8, 3);Date ret2=d2--;d2.Print();//2025,8,2ret2.Print();//2025,8,3
}int main()
{test();return 0;
}
--测试完成,打印结果符合预期,退出码为0
四.Date类比较符号的运算符重载实现
--我们的比较类符号有很多,==,>,>=,<,<=,!=。但我们只需要完整的实现其中几个剩下的通过复用和逻辑取反就可以了。
4.1 ==
// ==运算符重载
bool Date::operator==(const Date& d)
{return (_year == d._year) && (_month == d._month) && (_day == d._day);
}
--这里的逻辑比较简单,年月日都相等这个日期就是相等的了
4.2 >
// >运算符重载
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;// }// else {// return false;// }//}//else {// return false;//}//简化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;
}
--其实就是先判断年,看谁大,如果相等再判断月份。月份如果能比出来>就直接返回true,不能就继续判断天数,这里直接返回表达式结果就行。最后如果这些判断都不满足了,就返回false
4.3 >=
--通过复用==和>就能实现这个了,两者任意满足其中一个就行
// >=运算符重载
bool Date::operator >= (const Date& d)
{return (*this == d) || (*this > d);
}
4.4 <
--小于不就是>=的结果逻辑取反嘛
// <运算符重载
bool Date::operator < (const Date& d)
{return !(*this >= d);//>=的逻辑取反
}
4.5 <=
-- <=就是>的逻辑取反
// <=运算符重载
bool Date::operator <= (const Date& d)
{return !(*this > d);//>的逻辑取反
}
4.6 !=
-- !=就是==的逻辑取反
// !=运算符重载
bool Date::operator != (const Date& d)
{return !(*this == d);//==的逻辑取反
}
test.cpp:
#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"void test()
{Date d1(2025, 8, 3);Date d2(2025, 8, 5);Date d3(2025, 8, 3);//== 运算符重载cout << (d1 == d3) << '\n';//1cout << (d1 == d2) << '\n'<<'\n';//0//>运算符重载cout << (d1 > d3) << '\n';//0cout << (d1 > d2) << '\n';//0cout << (d2 > d1) << '\n'<<'\n';//1// >=运算符重载cout << (d1 >= d3) << '\n';//1,=cout << (d1 >= d2) << '\n';//0,<cout << (d2 >= d1) << '\n'<<'\n';//1,>//<运算符重载cout << (d1 < d3) << '\n';//0cout << (d1 < d2) << '\n';//1cout << (d2 < d1) << '\n'<<'\n';//0// <=运算符重载cout << (d1 <= d3) << '\n';//1,=cout << (d1 <= d2) << '\n';//1,<cout << (d2 <= d1) << '\n'<<'\n';//0,>//!= 运算符重载cout << (d1 != d3) << '\n';//0cout << (d1 != d2) << '\n'<<'\n';//1
}int main()
{test();return 0;
}
--测试完成,打印结果都符合预期(有点多),退出码为0
--1表示true,0表示false就不用多说了吧
五.Date类日期-日期的重载实现
--这个日期减日期,我们可以直接全部换算成天数来求,但是这样来很麻烦。所以还是使用下面这个方法会好点,还是先看代码。
日期-日期
// 日期-日期 返回天数
int Date::operator-(const Date& d)
{Date max = *this;Date min = d;int ans = 1;//符号int n = 0;if (*this < d){max = d;min = *this;ans = -1;}while (min != max){++min;++n;}return n * ans;
}
思路:
--先假设*this为较大值,d为较小值。此时的符号就是1(后面直接乘就是正数),但是我们需要进行判断一下,如果假设错了就直接在if语句里改正过来,再令符号标识为-1(后面乘就会为负数)。后续操作就是只要小的不等于大的就一直++min,n也一直++。直到相等,这时的n就是他们两之间相差的天数,然后乘以符号,就是我们想要的结果了
test.cpp:
#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"void test()
{// 日期-日期 返回天数Date d1(2025, 8, 3);Date d2(2025, 8, 5);int days1 = d1 - d2;//-2int days2 = d2 - d1;//2cout << days1 << '\n' << days2 << '\n';
}int main()
{test();return 0;
}
--测试完成,打印结果符合预期,退出码为0
六.代码总览
Date.h:
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;class Date
{
public:// 获取某年某月的天数int GetMonthDay(int year, int month){assert(month > 0 && month < 13);static int MonthDayArray[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;}else {return MonthDayArray[month];}}// 全缺省的构造函数Date(int year = 1900, int month = 1, int day = 1);// 拷贝构造函数// d2(d1)--this//Date(const Date& d);// 赋值运算符重载--其实也可以不写// d2 = d3 -> d2.operator=(&d2, d3)Date& operator=(const Date& d){{if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}}// 析构函数/*~Date();*/// 日期+=天数Date& operator+=(int day);// 日期+天数Date operator+(int day);// 日期-天数Date operator-(int day);// 日期-=天数Date& operator-=(int day);// 前置++Date& operator++();// 后置++Date operator++(int);// 后置--Date operator--(int);// 前置--Date& operator--();// >运算符重载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);// 日期-日期 返回天数int operator-(const Date& d);//打印void Print();private:int _year;int _month;int _day;
};
Date.cpp:
#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"// 全缺省的构造函数
Date::Date(int year, int month, int day)
{_year = year;_month = month;_day = day;
}//// 拷贝构造函数--可以不写
//// d2(d1)--this
//Date::Date(const Date& d)
//{
// _year = d._year;
// _month = d._month;
// _day = d._day;
//}//日期+=天数
Date& Date::operator+=(int day)
{_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);++_month;if (_month == 13){++_year;_month = 1;}}//this出了作用域还在,所以可以用引用返回,减少拷贝return *this;
}
//
//// 日期+天数
//Date Date::operator+(int day)
//{
// //加天数需要原来的不改变
// Date tmp(*this);
// //剩余逻辑和+=类似
// tmp._day += day;
// while (tmp._day > GetMonthDay(tmp._year, tmp._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)
{Date tmp(*this);tmp += day;return tmp;
}////+=复用+
//Date& Date::operator+=(int day)
//{
// *this = *this + day;
// return *this;
//}//最后我们可以得出用拷贝多的复用拷贝少的更好,也就是+复用+=// 日期-=天数
Date& Date::operator-=(int day)
{_day -= day;while (_day <= 0){_month--;if (_month == 0){_month = 12;--_year;}_day += GetMonthDay(_year, _month);}return *this;
}// 日期-天数
//直接复用
Date Date::operator-(int day)
{//-的话原来的不改变Date tmp(*this);tmp -= day;return tmp;
}// 前置++
Date& Date::operator++()
{//前置++先++后赋值,所以返回值是运算之后的*this += 1;return *this;
}// 后置++
Date Date::operator++(int)
{//后置++先赋值后++,所以返回值是运算之前的。//我们先把原来的保存下来Date tmp(*this);*this += 1;return tmp;
}// 前置--
Date& Date::operator--()
{//前置--先--后赋值,所以返回值是运算之后的*this -= 1;return *this;
}// 后置--
Date Date::operator--(int)
{//后置--先赋值后--,所以返回值是运算之前的。//我们先把原来的保存下来Date tmp(*this);*this -= 1;return tmp;
}// ==运算符重载
bool Date::operator==(const Date& d)
{return (_year == d._year) && (_month == d._month) && (_day == d._day);
}// >运算符重载
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;// }// else {// return false;// }//}//else {// return false;//}//简化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;
}// >=运算符重载
bool Date::operator >= (const Date& d)
{return (*this == d) || (*this > d);
}// <运算符重载
bool Date::operator < (const Date& d)
{return !(*this >= d);//>=的逻辑取反
}// <=运算符重载
bool Date::operator <= (const Date& d)
{return !(*this > d);//>的逻辑取反
}// !=运算符重载
bool Date::operator != (const Date& d)
{return !(*this == d);//==的逻辑取反
}// 日期-日期 返回天数
int Date::operator-(const Date& d)
{Date max = *this;Date min = d;int ans = 1;//符号int n = 0;if (*this < d){max = d;min = *this;ans = -1;}while (min != max){++min;++n;}return n * ans;
}//打印
void Date::Print()
{cout << _year << "-" << _month << "-" << _day << endl;
}//// 析构函数--可以不写
//Date::~Date()
//{
// _year = 0;
// _month = 0;
// _day = 0;
//}
test.cpp:
#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"void test()
{//// 日期+=天数//Date d1(2025, 8, 3);//Date d2=d1+=100;////d1.operator+=(100);//d1.Print();//d2.Print();//// 日期+天数//Date d3(2025, 8, 3);//Date d4= d3 + 100;////d3.operator+(100);//d3.Print();//d4.Print();//
/////////////////////////////////////////// 日期-=天数//Date d1(2025, 8, 3);//Date d2 = d1 -= 100;////d1.operator-=(100);//d1.Print();//d2.Print();//// 日期-天数//Date d3(2025, 8, 3);//Date d4 = d3 - 100;////d3.operator-=(100);//d3.Print();//d4.Print();//////////////////////////////////////////////// 前置++//Date d1(2025, 8, 3);//Date ret1 = ++d1;//d1.Print();//2025,8,4//ret1.Print();//2025,8,4////后置++//Date d2(2025, 8, 3);//Date ret2 = d2++;//d1.Print();//2025,8,4//ret2.Print();//2025,8,3/////////////////////////////////////////////////前置--//Date d1(2025, 8, 3);//Date ret1=--d1;//d1.Print();//2025,8,2//ret1.Print();//2025,8,2////后置--//Date d2(2025, 8, 3);//Date ret2=d2--;//d2.Print();//2025,8,2//ret2.Print();//2025,8,3/////////////////////////////////////////////////Date d1(2025, 8, 3);//Date d2(2025, 8, 5);//Date d3(2025, 8, 3);////== 运算符重载//cout << (d1 == d3) << '\n';//1//cout << (d1 == d2) << '\n'<<'\n';//0//////>运算符重载//cout << (d1 > d3) << '\n';//0//cout << (d1 > d2) << '\n';//0//cout << (d2 > d1) << '\n'<<'\n';//1////// >=运算符重载//cout << (d1 >= d3) << '\n';//1,=//cout << (d1 >= d2) << '\n';//0,<//cout << (d2 >= d1) << '\n'<<'\n';//1,>//////<运算符重载//cout << (d1 < d3) << '\n';//0//cout << (d1 < d2) << '\n';//1//cout << (d2 < d1) << '\n'<<'\n';//0//// <=运算符重载//cout << (d1 <= d3) << '\n';//1,=//cout << (d1 <= d2) << '\n';//1,<//cout << (d2 <= d1) << '\n'<<'\n';//0,>////!= 运算符重载//cout << (d1 != d3) << '\n';//0//cout << (d1 != d2) << '\n'<<'\n';//1/////////////////////////////////////////////////// 日期-日期 返回天数Date d1(2025, 8, 3);Date d2(2025, 8, 5);int days1 = d1 - d2;//-2int days2 = d2 - d1;//2cout << days1 << '\n' << days2 << '\n';
}int main()
{test();return 0;
}
完整源代码:
cpp-exclusive-warehouse: 【CPP知识学习仓库】 - Gitee.com
往期回顾:
《吃透 C++ 类和对象(上):封装、实例化与 this 指针详解》
《吃透 C++ 类和对象(中):构造函数与析构函数的核心逻辑》
《吃透 C++ 类和对象(中):拷贝构造函数与赋值运算符重载深度解析》
《吃透 C++ 类和对象(中):const 成员函数与取地址运算符重载解析》
结语:这篇博客我们实现了Date类,大家下去之后一定要自己试着完成一下,对后续衔接新的知识很有用,同时也是在复习前面学习的一些知识,其实大家应该可以发现,我们慢慢有了使用场景之后很多问题都清晰很多了,比如传引用返回什么时候可以用,为什么有时候可以用有时候不行,其逻辑是什么。诸如此类问题,随着我们不断的学习,我们最后都会有答案的。如果文章对你有帮助的话,欢迎评论,点赞,收藏加关注,感谢大家的支持。