一、为什么++运算符需要特殊处理
++是 C++ 中唯一(连同--)同时具有前置和后置两种形式的运算符:
前置:
++obj—— 先自增,再返回自增后的对象后置:
obj++—— 先返回原值,再自增
编译器无法仅凭函数名区分这两种形式,因此 C++ 规定用参数列表来区分:
| 形式 | 重载写法 | 特点 |
|---|---|---|
前置++obj | operator++() | 无参数 |
后置obj++ | operator++(int) | 多一个int占位参数(哑元) |
那个int参数没有任何实际用途,只是为了和前置版本在签名上区分开,调用时编译器会自动传0。
二、返回值设计的关键区别
这是面试和实际编码中最容易出错的地方:
前置++返回引用
cpp
T& operator++();
因为前置是"先加后用",返回的是对象自身,所以返回引用可以避免拷贝,也支持链式操作++++obj。
后置++返回值(临时对象)
cpp
T operator++(int);
因为后置必须返回自增前的旧值,而这个旧值是一个局部临时对象,函数结束后就销毁了,绝不能返回引用(否则返回悬空引用)。
#include <iostream> class Counter { private: int value; public: // 构造函数 Counter(int v = 0) : value(v) {} // 前置 ++ :++c Counter& operator++() { ++value; // 先自增 return *this; // 返回自身引用 } // 后置 ++ :c++ Counter operator++(int) { Counter old = *this; // 1. 保存旧值(拷贝) ++value; // 2. 自增 return old; // 3. 返回旧值(值返回,不是引用) } int getValue() const { return value; } }; int main() { Counter c(10); // 测试前置 ++ Counter& ref = ++c; std::cout << "++c 后: c = " << c.getValue() << ", 返回引用 = " << ref.getValue() << std::endl; // 测试后置 ++ Counter old = c++; std::cout << "c++ 后: c = " << c.getValue() << ", 返回旧值 = " << old.getValue() << std::endl; return 0; }