目录
[一、前置 ++ 和后置 ++ 的区别](#一、前置 ++ 和后置 ++ 的区别)
[四、前置 -- 的实现](#四、前置 -- 的实现)
[五、后置 -- 的实现](#五、后置 -- 的实现)
一、前置 ++ 和后置 ++ 的区别
在C++中,++ 有两种形式。
cpp
++d; // 前置++
d++; // 后置++
对于日期类来说:
cpp
++d;
表示日期加一天,并返回加完后的对象。
cpp
d++;
也表示日期加一天,但返回的是加之前的旧值。
所以两者的区别是:
cpp
前置++:先加,再返回当前对象
后置++:先保存旧值,再加,最后返回旧值
二、前置++的实现
前置++ 可以直接复用前面写过的 operator+=。
因为日期加一天,本质就是:
cpp
*this += 1;
代码:
cpp
Date& operator++() {
return *this += 1;
}
这里返回值是 Date&,也就是当前对象的引用。
原因是:
cpp
前置++修改的是当前对象,返回修改后的当前对象
三、后置++的实现
后置++需要返回加之前的旧值。
所以实现步骤是:
cpp
1. 保存当前对象的副本。
2. 当前对象加一天;
3. 返回保存的旧值。
代码:
cpp
Date operator++(int) {
Date tmp = *this;
*this += 1;
return tmp;
}
这里的 int 参数没有实际使用,他只是来区分前置和后置。
也就是说:
cpp
operator++(); // 前置++
operator++(int) // 后置++
后置 ++ 返回的是值,所以不是引用。
因为 tmp 是函数内部的对象,函数结束后会销毁,不能返回它的引用。
四、前置 -- 的实现
前置 -- 和前置 ++ 类似。
前置 -- 表示日期减一天,先减,再返回当前对象。
可以复用 operator-=:
cpp
Date& operator--() {
return *this -= 1;
}
五、后置 -- 的实现
后置 -- 表示日期减一天,但返回减之前的旧值。
代码:
cpp
Date operator--(int) {
Date tmp = *this;
*this -= 1;
return tmp;
}
这里同样使用 int 占位参数区分前置和后置。
六、日期减日期的意义
日期类中,两个日期相加通常是没有实际意义的。
例如:
cpp
2024-5-1 + 2024-6-1
这个结果就没有明确意义。
但是两个日期相减是有意义的:
cpp
2024-6-1 - 2024-5-1
表示两个日期差多少天。
所以日期类中可以重载:
cpp
int operator-(const Date& d);
返回两个日期相差的天数。
七、日期减日期的实现思路
直接做年月日结尾计算比较麻烦。
比如:
cpp
2024-1-10 - 2023-12-20
涉及跨年、跨月、不同月份天数、闰年等问题。
一种比较容易理解的思路是:
让比较小的日期不断加一天,直到等于较大的日期,累计加了多少次,就相差多少天。
例如:
cpp
2024-5-1 和 2024-5-4
从 2024-5-1 开始:
cpp
+1 -> 2024-5-2
+1 -> 2024-5-3
+1 -> 2024-5-4
一共加了 3 次,所以相差 3 天。
这种方法逻辑很简单,而且可以复用前面已经实现的 ++ 和比较运算符。
八、处理正负号
日期减日期还要考虑正负。
例如:
cpp
d1 - d2
如果 d1 > d2,结果就应该是正数。
如果 d1 < d2,结果就应该是负数。
可以用一个 flag 标记符号:
cpp
如果左边日期较大,flag = 1
如果左边日期较小,flag = -1
最后返回:
cpp
count * flag
九、日期减日期代码示例
假设已经实现了:
cpp
bool operator<(const Date& d) const;
bool operator!=(const Date& d) const;
Date& operator++();
那么日期减日期可以这样写:
cpp
int operator-(const Date& d) const {
Date max = *this;
Date min = d;
int flag = 1;
if (*this < d) {
max = d;
min = *this;
flag = -1;
}
int count = 0;
while (min != max) {
++min;
++count;
}
return count * flag;
}
这段代码的逻辑是:
cpp
1. 先假设当前对象是较大日期;
2. 如果当前对象小于 d,就交换 max 和 min;
3. 让 min 不断前置 ++;
4. 每加一天,count 加 1;
5. min 等于 max 停止;
6. 最后乘以 flag 修正正负号。
十、为什么使用前置++?
在循环中:
cpp
++min;
优先使用前置++,而不是后置++:
cpp
min++;
原因是后置 ++ 需要保存旧值,会产生临时对象。
而这里不需要旧值,只需要让日期加一天。
所以使用前置 ++ 更合适。
前面也提到,后置操作因为涉及临时对象的拷贝,性能低于前置操作,因此在不需要旧值时优先使用前置形式。
十一、完整示例片段
这里只展示和本篇相关的核心函数:
cpp
class Date {
public:
Date& operator<(const Date& d) const;
Date& operator!=(xonst Date& d) const;
Date& operator+=(int day);
Date& operator-=(int day);
Date& operator++() {
return *this += 1;
}
Date operator++(int) {
Date tmp = *this;
*this += 1;
return tmp;
}
Date& operator--() {
return *this -= 1;
}
Date operator--(int) {
Date tmp = *this;
*this -= 1;
return tmp;
}
int operator-(const Date& d) const {
Date max = *this;
Date min = d;
int flag = 1;
if (*this < d) {
max = d;
min = *this;
flag = -1;
}
int count = 0;
while (min != max) {
++min;
++count;
}
return count * flag;
}
private:
int _year;
int _month;
int _day;
};
十二、小结
本篇主要学习了日期类中的前置后置 ++/-- 和日期减日期。
需要记住:
- 前置 ++ 是先加,在返回当前对象;
- 后置 ++ 是先保存旧值,再加,返回旧值;
- 后置 ++ 通过 int 占位参数和前置 ++ 区分;
- 前置 ++ 返回当前对象的引用;
- 后置 ++ 返回旧值,不能返回局部对象引用;
- -- 的实现和 ++ 类似,只是方向相反;
- 日期减日期表示两个日期相差多少天;
- 直接借位计算较复杂,可以采用"小日期加和到大日期"的方式;
- 使用 flag 处理结果正负;
- 不需要旧值时,优先使用前置 ++,减少临时拷贝。
日期类是练习运算符重载的经典案例。通过这些运算符的实现,可以更好的理解值返回、引用返回、临时对象、代码复用以及边界处理。