目录
[一 回顾与引入](#一 回顾与引入)
[二 实现-和-=运算符重载](#二 实现-和-=运算符重载)
[三 实现前置--和后置--的运算符重载](#三 实现前置--和后置--的运算符重载)
[四 日期比较大小](#四 日期比较大小)
[五 实现日期减日期](#五 实现日期减日期)
[六 流插入操作符的运算符重载](#六 流插入操作符的运算符重载)
[七 流提取操作符夫人运算符重载](#七 流提取操作符夫人运算符重载)
[八 完整代码](#八 完整代码)
往期回顾:
博主主页有收录之前写的C++专栏,感兴趣的uu可以看看
一 回顾与引入
我们在上一篇中学到了运算符重载,当运算符被用于类类型的对象时,C++语言允许我们通过运算符重载的形式指定新的含义。C++规 定类类型对象使用运算符时,必须转换成调用对应运算符重载,若没有对应的运算符重载,则会编译报错。
在上一篇最后我们学习了用日期类实现 += 和 **+**的运算符重载,以下是回顾。
cpp
int GetMonthDay(int year, int month)
{
assert(month > 0 && month < 13);
static int monthDayArray[13] = { -1, 31, 28, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31 };
// 365天 5h +
if (month == 2 && (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
{
return 29;
}
else
{
return monthDayArray[month];
}
}
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;
}
//d1 + 100
Date& Date::operator+(int day)
{
Date tmp(*this);
tmp += tmp;
return tmp;
}
那么我们可以通过上述的实现,引入**-=** 和**-**的运算符重载
二 实现-和-=运算符重载
-和-= 的区别是:-不能改变自己,而-= 是改变自己

注意:在减去一个日期的时候,如果当前月的日期不够,则需要向上一个月借日期。例如上述的例子:8-50肯定不够,那我们就需要向上一个月借一个月的日期,上一个月是八月,那就是31天。这里有一个易错点:要减去的是上一个月的日期而不是这个月的日期!!!!
下面实现:
Date.cpp
cpp
Date& Date::operator-=(int day)
{
if (day < 0)
{
return *this += -day;
}
_day -= day;
while (_day <= 0)
{
--_month;
if (_month == 0)
{
--_year;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
Date Date::operator-(int day)
{
Date tmp = *this;
tmp -= day;
return tmp;
}
Test.cpp:
cpp
void TestDate1()
{
Date d1(2025, 9, 8);
d1 -= 50;
d1.Print();
Date d2 = d1 - 50;
d2.Print();
Date d3(2025, 9, 8);
d3 -= 2000;
d3.Print();
Date d4(2025, 9, 8);
d4 -= -50;
d4.Print();
}
注意:
1:在-=的运算符重载函数中:第一个while循环的条件为_day <= 0,循环的条件是终止循环的条件,相当于当_day > 0时,跳出循环
2 :在**-**的运算符重载里,没有使用传引用返回。因为使用了临时变量tmp
3:在传参时,参数是int类型,就意味着可以传正数,也可以传负数。需要判断传入的day是否为正。同理可得,在+=中,也需要判断day是否为正。
cpp
if(day < 0)
{
return *this += -day;
}
这段代码的意思是:如果day <0,减去了一个负数,就相当于加一个正数(ps:day前是负号)
所以,+=的运算符重载修改为:
cpp
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)
{
++_year;
_month = 1;
}
}
return *this;
}
三 实现前置--和后置--的运算符重载
在定义前置--和后置--的运算符重载时,我们发现都是 operator--(),如果这样写的话就无法区分了。所以祖师爷在设定的时候规定后置--要传Int: operator--(int),同理得,前置++和后置++也适用。
cpp
//前置
Date& Date::operator--()
{
*this-=1;
return *this;
}
//后置
Date Date::operator--(int)
{
Date tmp = *this;
*this -= 1;
return tmp;
}
在调用时,优先推荐调用前置,前置的效率较高。后置会有两次拷贝
四 日期比较大小
以下是我们要实现的有关日期比较大小的运算符重载,我们来一个一个实现
cpp
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;
(1) <
我们只需要列举出所有为真的情况,其余情况全为假
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;
}
年小则小,年相等,月小则小。年,月相等,日小则小。
同理可得:实现>时,可以直接将<的代码拿过来,修改其中的符号即可,如下所示:
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;
}
我们发现,这两段代码的重复度非常的高,那我们能不能用 <的运算符重载直接实现>的运算符重载呢?
答案是:可以
cpp
// d1 <= d2
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 _year == d._year
&& _month == d._month
&& _day == d._day;
}
bool Date::operator!=(const Date& d)
{
return !(*this == d);
}
如上所示,例如实现 <=的时候,就相当于实现小于或等于,而这两个运算符的重载我们可以直接拿过来复用 。(大于就是小于等于取反)
这样实现的好处:例如我们要给日期类增加时,分,秒 如果不使用复用的话,就要一次性修改六个函数,但是使用了现在的逻辑,就可以只修改 < 和 =的函数,其他的函数都是他们俩的复用,不需要修改。
复用的好处:增加可维护性
五 实现日期减日期
思路1:

将第一个日期和第二个日期都和这一年的一月一号相减,分别得到一个a月b天,转化为具体的天数,分别为x天和y天。将两个日期的年份相减,得到一个z年,所以两个日期相减之后的天数就是:z*365 + 跨越闰年数 + (x-y)
注:闰年是366天
思路2:
比较两个日期的大小,小的日期不断++,直到和大的日期相等,++了多少次,他们就相差了多少天
我们实现思路2:
cpp
// d1 - d2
int Date::operator-(const Date& d)
{
Date max = *this;
Date min = d;
int flag = 1;
if (*this < d)
{
min = *this;
max = d;
flag = -1;
}
int day = 0;
while (min != max)
{
++min;
++day;
}
return day * flag;
}
我们先假设d1是max,d2是min(this是d1,d是d2),如果为真,则d1-d2一定是一个正数。如果为假,则d1-d2一定是一个负数,此时我们就用flag标记结果是为正还是为负
test.cpp:
cpp
void TestDate2()
{
Date d4(2025, 9, 8);
Date d5(2025, 10, 1);
cout << d4 - d5 << endl;
Date d6(2025, 12, 1);
cout << d4 - d6 << endl;
Date d7(2029, 7, 1);
cout << d4 - d7 << endl;
}
六 流插入操作符的运算符重载
流插入操作符为**<<**
流插入操作符<<
的运算符重载常用于自定义对象的输出,使我们可以像输出基本数据类型一样输出自定义对象。

流插入的cout是属于ostream类型的
我们先来写一下看看:
cpp
void operator<<(ostream& out)
{
out << _year << "/" <<_month <<"/" << _day << '\n';
}
void TestDate3()
{
Date d1(2025,9,8);
//cout << d1
d1.operator << (cout);
d1 << cout;
}
运行结果为:
cpp
2025/9/8
这样乍一看没什么问题,但是流插入应该是日期类d1流向控制台cout,但在这里变成了由控制台流向日期类,为什么呢?
我们在前面学习运算符重载的性质的时候学到:
重载运算符函数的参数个数和该运算符作用的运算对象数量⼀样多。⼀元运算符有⼀个参数,⼆元 运算符有两个参数,⼆元运算符的左侧运算对象传给第⼀个参数,右侧运算对象传给第⼆个参数。
也就是d1传给了ostream,cout传给了out

说明流插入不能这么写。写在成员函数里,第一个参数就会被占(成员函数的this指针是隐藏的),cout只能变成右操作数到第二个参数去。虽然这样代码也能跑,但是不符合可读性,而运算符重载就是为了增强可读性,我们期望的是cout占据第一个参数
但是我们可以不放置到成员函数中,当初放置到成员函数中是为了访问私有的成员变量,但是现在成员函数不符合我们的预期,所以把这个函数放置到全局。
cpp
void operator<<(ostream& out,comst Date d)
{
out << d._year << "/" << d._month << "/" << d._day << '\n';
}
void TestDate3()
{
Date d1(2025,9,8);
operator(cout,d1);
cout << d1;
}
注意:此处的out是cout的别名,也可以用别的替代,但是别名尽量不要用cout,理论上讲形参和实参可以同名,但是这里用cout作为别名可能会和库里的冲突
这样写就可以了,但是放置在全局此时我们就没办法访问私有成员了,怎么办?
我们就用到了**友元函数,**但是友元函数我们下一篇再讲解,先在这里简单提一下。友元函数其实很简单,只需要再类里加上友元函数的声明就可以了
cpp
class Date:
{
//友元函数的声明
friend void operator<<(ostream& out,ostream& d);
//..........
}
friend是一个关键字
其他情况:我们在输出的时候有可能会连续的输出,例如:
cpp
cout << d1 << d2;
但是程序不允许:

为什么呢:连续的输出是从左到右,就和连续赋值一样,这个调用就会转化成调用流插入函数,第一个cout << d1的返回值继续调用 << d2,所以第一个调用的返回值应该是cout,此时就需要修改函数,使他的返回值是cout
cpp
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "/" << d._month << "/" <<d._day << '\n';
return out;
}
注意:这里改了之后,友元函数声明那里也要修改
七 流提取操作符夫人运算符重载
cin是istream类型的对象

cin和cout一样,也需要占据第一个参数的位置
cpp
istream& operator>>(istream& in, Date& d)
{
in>>d._year >>d._month >> d._day;
}
注意:这里的第二个参数和cout不同,此处没有const修饰。因为这里是将数据读到d中,d需要时可修改的
但是当前程序还存在一些缺陷。我们再定义和实现的时候可能会输入一些非法的日期,而这些日期再进行一些运算就没有意义,所以我们可以写一个日期类的检查,检查一个日期是否合法
cpp
bool CheckDate()
{
if (_month < 1 || _month > 12)
return false;
if (_day < 1 || _day > GetMonthDay(_year, _month))
return false;
return true;
}
这里cin就可以加上检查函数:
cpp
istream& operator>>(istream& in, Date& d)
{
while (1)
{
cout << "请依次输入年月日:>";
in >> d._year >> d._month >> d._day;
if (d.CheckDate())
{
break;
}
else
{
cout << "输入日期非法,请重新输入" << endl;
}
}
return in;
}
八 完整代码
Date.h
cpp
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
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();
int GetMonthDay(int year, int month)
{
assert(month > 0 && month < 13);
int monthDayArray[13] = { -1, 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];
}
}
bool CheckDate()
{
if (_month < 1 || _month > 12)
return false;
if (_day < 1 || _day > GetMonthDay(_year, _month))
return false;
return true;
}
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);
// d1 += 天数
Date& operator+=(int day);
Date operator+(int day);
// d1 -= 天数
Date& operator-=(int day);
Date operator-(int day);
// d1 - d2
int operator-(const Date& d);
// ++d1 -> d1.operator++()
Date& operator++();
// d1++ -> d1.operator++(0)
// 为了区分,构成重载,给后置++,强行增加了一个int形参
// 这里不需要写形参名,因为接收值是多少不重要,也不需要用
// 这个参数仅仅是为了跟前置++构成重载区分
Date operator++(int);
// 前置
Date& operator--();
// 后置
Date operator--(int);
// d1.operator<<(cout);
/*void operator<<(ostream& out)
{
out << _year << "/" << _month << "/" << _day << '\n';
}*/
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);
Date.cpp
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
if (!CheckDate())
{
cout << "非法日期:>" << *this;
}
}
void Date::Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
// d1 += 100
//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;
//}
//
//// d1 + 100
//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;
//}
// d1 += 100
// d1 += -100
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)
{
++_year;
_month = 1;
}
}
return *this;
}
// d1 + 100
Date Date::operator+(int day)
{
Date tmp(*this);
tmp += day;
return tmp;
}
// d1 += 100
//Date& Date::operator+=(int day)
//{
// *this = *this + day;
// return *this;
//}
//
//// d1 + 100
//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;
//}
// d1 -= 100 20:05
// d1 -= -100
Date& Date::operator-=(int day)
{
if (day < 0)
{
return *this += -day;
}
_day -= day;
while (_day <= 0)
{
--_month;
if (_month == 0)
{
--_year;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
// d1 - 100
Date Date::operator-(int day)
{
Date tmp = *this;
tmp -= day;
return tmp;
}
// ++d1 -> d1.operator++();
Date& Date::operator++()
{
*this += 1;
return *this;
}
// d1++ -> d1.operator++(0);
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)
{
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)
{
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 _year == d._year
&& _month == d._month
&& _day == d._day;
}
bool Date::operator!=(const Date& d)
{
return !(*this == d);
}
// d1 - d2 -> 9:12
int Date::operator-(const Date& d)
{
Date max = *this;
Date min = d;
int flag = 1;
if (*this < d)
{
min = *this;
max = d;
flag = -1;
}
int day = 0;
while (min != max)
{
++min;
++day;
}
return day * flag;
}
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "/" << d._month << "/" << d._day << '\n';
return out;
}
istream& operator>>(istream& in, Date& d)
{
while (1)
{
cout << "请依次输入年月日:>";
in >> d._year >> d._month >> d._day;
if (d.CheckDate())
{
break;
}
else
{
cout << "输入日期非法,请重新输入" << endl;
}
}
return in;
}
Test.cpp
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
void TestDate1()
{
Date d1(2025, 9, 8);
d1 -= 50;
d1.Print();
Date d2 = d1 - 50;
d2.Print();
Date d3(2025, 9, 8);
d3 -= 2000;
d3.Print();
Date d4(2025, 9, 8);
d4 -= -50;
d4.Print();
}
void TestDate2()
{
Date d1(2025, 9, 8);
//Date d2 = d1.operator--();
Date d2 = --d1; // d1.operator--();
d1.Print();
d2.Print();
//Date d3 = d1.operator--(1);
Date d3 = d1--; // d1.operator--(1);
d1.Print();
d3.Print();
Date d4(2025, 9, 8);
Date d5(2025, 10, 1);
cout << d4 - d5 << endl;
Date d6(2025, 12, 1);
cout << d4 - d6 << endl;
Date d7(2029, 7, 1);
cout << d4 - d7 << endl;
}
void TestDate3()
{
Date d1(2025, 9, 8);
Date d2(2025, 9, 90);
//operator<<(cout, d1);
cout << d1 << d2;
// 虽然可以跑,但是不符合可读性
//d1.operator<<(cout);
//d1 << cout;
cin >> d1 >> d2;
cout << d1 << d2;
}
int main()
{
TestDate3();
//int i = 1;
//double d = 1.1;
//cout << i; // cout.operator<<(i)
//cout << d; // cout.operator<<(d)
return 0;
}