在这里插入图片描述

传送门:日期计算器(网页版)

基本构造实现

获取某年某月的天数

1
2
3
4
5
6
7
8
9
10
int GetMonthDay(int year, int month)
{
static int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30,31 };
int day = days[month];
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
{
day += 1;
}
return day;
}

口诀:(四年一润,百年不润) 或者 四百年一润

构造函数

1
2
3
4
5
Date(int year = 1900, int month = 1, int day = 1)
:_year(year)
,_month(month)
,_day(day)
{}

用于初始化创建的每一个实例

拷贝构造函数

1
2
3
4
5
6
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}

通常拷贝构造函数用于用一个已经存在的对象初始化另一个对象,如Date d3(d1),等价于Date d3 = d1

但是像d1 = d2两个已经存在的对象之间的复制,调用的是=的运算符重载,而不是拷贝构造

具体实现解析可以看往期博客:

传送门:C++天使的灵动心跳代码:类和对象(中下)

打印函数

1
2
3
4
void Print()
{
cout << _year << '-' << _month << '-' << _day << endl;
}

用于打印日期类

运算符重载接口实现

日期比较

<运算符重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
bool Date::operator < (const Date& x)
{
if (_year < x._year)
{
return true;
}
else if (_year == x._year && _month < x._month)
{
return true;
}
else if (_year == x._year && _month == x._month && _year < x._year)
{
return true;
}

return false;
}

实现日期小于的比较,我们要先思考日期的比较需要分别比较年月日,从年开始依次比较,一旦成立则说明小于成立;反之应该是大于,小于不成立

🔥值得注意的是:

  1. 传参时要特别注意,运算符有几个操作数,就传几个参数this指针也算在参数中
    所以这里默认<左边的对象是用this指针传过去了,右边的对象引用传参(加const避免被传的数值被修改,引用传参提高效率)
  2. 这里的运算符重载函数是成员函数

在这里插入图片描述

调试状态下,转到反汇编比较可以观察到,两种写法的底层代码是一样的,说明了d1 < d2会转换成d1.operator<(d2)

如果是非成员函数的情况下呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
bool Date::operator < (const Date& x1, const Date& x2)
{
if (x1._year < x2._year)
{
return true;
}
else if (x1._year == x2._year && x1._month < x2._month)
{
return true;
}
else if (x1._year == x2._year && x1._month == x2._month && x1._year < x2._year)
{
return true;
}

return false;
}

注意在类内要添加友元的声明,在类外定义运算符重载函数时,要传两个参数,原因在于非成员函数没有隐含的 this 指针来指向调用对象。不像成员函数,通过 this 指针隐式获取左侧对象,只需要显式接收右侧对象作为参数

在这里插入图片描述
同样转到反汇编比较,可以观察到非成员函数下,d1 < d2会转换成operator<(d1, d2),底层代码也清晰的指出要传几个参数

==运算符重载

1
2
3
4
5
6
bool Date::operator==(const Date& x)
{
return _year == x._year
&& _month == x._month
&& _day == x._day;
}

==的运算符重载很简单,直接返回即可,如果相等返回true反之则为false

<=运算符重载

1
2
3
4
bool Date::operator<=(const Date& x)
{
return *this < x || *this == x;
}

显而易见,注意左边的数是由this指针隐式传递,满足小于或等于都能成立

>运算符重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
bool Date::operator > (const Date& x)
{
if (_year > x._year)
{
return true;
}
else if (_year == x._year && _month > x._month)
{
return true;
}
else if (_year == x._year && _month == x._month && _year > x._year)
{
return true;
}

return false;
}

大于的运算符重载和小于的同理,修改符号即可

但是这样写实在是太麻烦了,根据上面写的==的运算符重载>=运算符重载<运算符重载,可以极大的简化和日期类比较有关的函数

1
2
3
4
bool Date::operator > (const Date& x)
{
return !(*this <= x);
}

<=的反面就是>,借助这一点能够减少很多不必要的代码

>=运算符重载

1
2
3
4
bool Date::operator>=(const Date& x)
{
return !(*this < x);
}

同理,借助刚写完的>运算符重载也能简单的写出>=运算符重载函数

!=运算符重载

1
2
3
4
bool Date::operator!=(const Date& x)
{
return !(*this == x);
}

==运算符重载取反即可

日期与天数的计算

日期+=天数

在这里插入图片描述
首先我们要明白日期+天数如何计算,那么经过举例演算可以发现,用进位的方式实现日期+天数,简单来说就是天满了往月进,月满了往年进,依赖于GetMonthDay函数处理每个月的天数,不断减掉天数直到合法为止

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Date& Date::operator+=(int day)
{
if (day < 0)
{
*this -= -day;
}
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
++_month;
if (_month == 13)
{
++_year;
_month = 1;
}
}
return *this;
}

因为我们是在原本的日期上操作,所以是+=引用返回,而不是+,注意加上负数等同于减去正数,也可以用abs取绝对值

1
2
3
4
5
Date& Date::operator+=(int day)
{
*this = *this + day;
return *this;
}

+=还可以利用下面实现的日期+天数的运算来写

2.2.2 日期+天数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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)
{
++_year;
tmp._month = 1;
}
}
return tmp;
}

那么真正的日期+天数,应该是不会修改原来的日期的,而是以另一个变量返回,所以这里就可以利用拷贝构造,将一个新的日期返回,注意不能引用返回,因为tmp是局部变量,出了作用域就被销毁了

1
2
3
4
5
6
7
Date Date::operator+(int day)
{
Date tmp(*this);
tmp += day;

return tmp;
}

同理+也可以利用日期+=天数来运算,但是这两种简洁的写法不可能同时存在,因为至少要有一种是完整实现了的,那么就可以对两种组合进行比对

在这里插入图片描述
考虑到变量的拷贝以及创建,明显是创建完整的+=运算,然后据此写+运算更优,因为这个组合创建的对象更少减少对拷贝构造的调用

日期−=天数

在这里插入图片描述
举一反三,既然日期+天数是进位,那么日期-天数就是借位,每当被减成负数的时候,要注意是向上个月借天数不是本月的天数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Date& Date::operator-=(int day)
{
if (day < 0)
{
*this += -day;
}
_day -= day;
while (_day <= 0)
{
--_month;
if (_month == 0)
{
_month = 12;
--_year;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}

+=的写法其实大差不差,注意减去负数就是加上正数

日期−天数

1
2
3
4
5
6
Date Date::operator-(int day)
{
Date tmp(*this);
*this -= day;
return tmp;
}

利用-=运算减少不必要的算法,记得保存原先的日期

日期的自增自减

前置++

1
2
3
4
5
Date& Date::operator++()
{
*this += 1;
return *this;
}

+=运算的前提下,日期也是能实现自增的,所以不用担心自增后会不会越过当月的天数,在+=已经处理好了

后置++

1
2
3
4
5
6
Date Date::operator++(int)
{
Date tmp = *this;
*this += 1;
return tmp;
}

后置++需要提前保存值,因为是先使用再++

🔥值得注意的是:

  1. 前置++后置++虽然是运算符重载,但是形式上也构成函数重载
  2. 后置++增加这个int参数不是为了接受具体的值仅仅是占位,跟前置++构成重载
  3. int这个位置传任何值都可以,实际调用的时候前置++后置++可能分别为d1.operator++()d1.operator++(0)括号内的值是随机的

前置−−

1
2
3
4
5
Date& Date::operator--()
{
*this -= 1;
return *this;
}

−−的操作和++几乎是一样的,这里就不过多赘述了

后置−−

1
2
3
4
5
6
Date Date::operator--(int)
{
Date tmp = *this;
*this -= 1;
return tmp;
}

日期-日期

通常日期的计算我们一般是用来计算两个日期之前相差多少天,因此我们的运算符重载是否有意义决定了是否要创建这个重载,所以只考虑日期-日期日期+日期是没有意义的
在这里插入图片描述
根据前面的算法,我们也能很容易总结出计算方法,把年份大的的日期的月份天数假设成和一样的年份小的一样,然后两者作天数差的计算即可

其实还有更简单的思路,就是让年份小的日期一天一天去++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int Date::operator-(const Date& d)
{
Date max = *this;
Date min = d;
int flag = 1;
if (*this < d)
{
max = d;
min = *this;
flag = -1;
}
int n = 0;
while (min != max)
{
++min;
++n;
}
return n * flag;
}

这里最妙的是这个flag,能够刚好体现出计算的正负,虽然这种方式没有上面那种来的快,但是这种程度的差距对于计算机来说其实是可以忽略不计的

日期的输入输出

基本数据类型基于cout和cin的使用能够很方便的进行输入输出的操作,那么日期依托于运算符重载也能达到此类效果,coutcin分别属于iostram库里的ostreamistream

<<运算符重载

根据前面的经验,我们依旧把<<运算符重载放在成员函数里

1
2
3
4
void Date::operator<<(ostream& out)
{
out << d._year << "年" << d._month << "月" << d._day;
}

可是当我们输出的时候就报错了,这是因为正常情况下cout<<d1会转化成d1.operator<<coutDate对象默认占用第一个参数,也就是做了左操作数,所以正确的写法应该是d1<<cout才能正常输出,但是这样太奇怪了,完全不符合我们的使用习惯

1
2
3
4
5
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "年" << d._month << "月" << d._day;
return out;
}

既然不能写成成员函数,那就在类外写成非成员函数,就可以带入两个参数,cout<<d1就会转化成operator<<(cout,d1),符合书写情况

🔥值得注意的是:

  1. cout从左往右连续输出,所以要返回ostream类型才能连续输出
  2. 类外访问私有变量使用友元函数就能解决
  3. ostream& out不能加const,因为流插入就是不断改变cout的过程

>>运算符重载

1
2
3
4
5
istream& operator>>(istream& in, const Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}

同理,流输入也是同样的格式

代码展示

Date.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <iostream>
#include <cstdbool>
using namespace std;

class Date
{
public:
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& out, const Date& d);
// 计算每个月的天数
int GetMonthDay(int year, int month)
{
static int days[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30,31 };
int day = days[month];
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
{
day += 1;
}
return day;
}

// 全缺省的构造函数
Date(int year = 1900, int month = 1, int day = 1)
:_year(year)
,_month(month)
,_day(day)
{}

// 拷贝构造函数
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
// d2(d1)

//打印日期
void Print()
{
cout << _year << '-' << _month << '-' << _day << endl;
}

bool operator < (const Date& x);
bool operator > (const Date& x);
bool operator >= (const Date& x);
bool operator <= (const Date& x);
bool operator==(const Date& x);
bool operator!=(const Date& x);
Date& operator+=(int day);
Date operator+(int day);
Date& operator-=(int day);
Date operator-(int day);
Date& operator++();
Date operator++(int);
Date& operator--();
Date operator--(int);
int operator-(const Date& d);

private:
int _year;
int _month;
int _day;
};

Date.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
//1.<运算符重载
bool Date::operator < (const Date& x)
{
if (_year < x._year)
{
return true;
}
else if (_year == x._year && _month < x._month)
{
return true;
}
else if (_year == x._year && _month == x._month && _year < x._year)
{
return true;
}

return false;
}

//2.>的运算符重载
bool Date::operator > (const Date& x)
{
if (_year > x._year)
{
return true;
}
else if (_year == x._year && _month > x._month)
{
return true;
}
else if (_year == x._year && _month == x._month && _year > x._year)
{
return true;
}

return false;
}

bool Date::operator > (const Date& x)
{
return !(*this <= x);
}

//3.=的运算符重载
///*void operator=(const Date& d)
//{
// _year = d._year;
// _month = d._month;
// _day = d._month;
//}*/
//Date& Date::operator=(const Date& d)
//{
// if (this != &d)
// {
// _year = d._year;
// _month = d._month;
// _day = d._day;
// }
// return *this;
//}

//4.==的运算符重载
bool Date::operator==(const Date& x)
{
return _year == x._year
&& _month == x._month
&& _day == x._day;
}

//5.<=的运算符重载
bool Date::operator<=(const Date& x)
{
return *this < x || *this == x;
}

//6.>=的运算符重载
bool Date::operator>=(const Date& x)
{
return !(*this < x);
}

//7.!=的运算符重载
bool Date::operator!=(const Date& x)
{
return !(*this == x);
}

//8.+=的运算符重载
Date& Date::operator+=(int day)
{
if (day < 0)
{
*this -= -day;
}
//*this = *this + day;
//return *this;
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
++_month;
if (_month == 13)
{
++_year;
_month = 1;
}
}
return *this;
}

//9.+的运算符重载
Date Date::operator+(int day)
{
Date tmp(*this);
tmp += day;

return tmp;

/*tmp._day += day;
while (tmp._day > GetMonthDay(tmp._year, tmp._month))
{
tmp._day -= GetMonthDay(tmp._year, tmp._month);
++tmp._month;
if (tmp._month == 13)
{
++_year;
tmp._month = 1;
}
}
return tmp;*/
}

//10.前置++
Date& Date::operator++()
{
*this += 1;
return *this;
}

//11.后置++
Date Date::operator++(int)
{
Date tmp = *this;
*this += 1;
return tmp;
}

//12.-=的运算符重载
Date& Date::operator-=(int day)
{
if (day < 0)
{
*this += -day;
}
_day -= day;
while (_day <= 0)
{
--_month;
if (_month == 0)
{
_month = 12;
--_year;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}

//13.-的运算符重载
Date Date::operator-(int day)
{
Date tmp(*this);
*this -= day;
return tmp;
}

//14.前置--
Date& Date::operator--()
{
*this -= 1;
return *this;
}

//15.后置--
Date Date::operator--(int)
{
Date tmp = *this;
*this -= 1;
return tmp;
}

//16.日期-日期
int Date::operator-(const Date& d)
{
Date max = *this;
Date min = d;
int flag = 1;
if (*this < d)
{
max = d;
min = *this;
flag = -1;
}
int n = 0;
while (min != max)
{
++min;
++n;
}
return n * flag;
}

//17.输出日期
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "年" << d._month << "月" << d._day;
return out;
}

//18.输入日期
istream& operator>>(istream& in, const Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}

希望读者们多多三连支持

小编会继续更新

你们的鼓励就是我前进的动力!

请添加图片描述