前置++返回int&(左值引用),后置++返回int(右值);自定义类型中后置需保存原值再返回副本,避免返回局部引用;混用i++与++i在复合表达式中属未定义行为;迭代器优先用前置++以避免额外拷贝;bool++语义反直觉,不应用于取反。

前置++和后置++的返回值类型不同
前置自增 ++i 返回的是 int&(左值引用),可以继续赋值或取地址;后置自增 i++ 必须返回原值的副本,所以返回 int(右值)。这是重载时最常出错的地方——如果后置版本也返回引用,会导致返回局部变量引用的未定义行为。
自定义类型中,典型实现是:
class Counter {
int val_;
public:
Counter& operator++() { // 前置
++val_;
return *this;
}
Counter operator++(int) { // 后置,int 是哑元参数
Counter old = *this;
++val_;
return old;
}
};
- 后置版本的参数
int不可省略,否则会和前置重载冲突 - 必须用
old保存原始状态再修改,不能写成return Counter{val_++}(那会调用后置++,死循环) - 返回
Counter而非Counter&,避免返回栈上临时对象的引用
在表达式中混用时行为差异明显
很多人以为 i++ + ++i 有确定结果,其实它是未定义行为(C++17 之前),因为两个副作用(两次修改 i)之间没有序列点。即使在 C++17 中,i++ + ++i 仍不合法——i++ 的值计算和副作用顺序独立于 ++i,编译器可自由调度。
安全写法是拆开:
立即学习“C++免费学习笔记(深入)”;
int i = 5; int a = i++; // a=5, i=6 int b = ++i; // b=7, i=7
-
i++整个表达式的值是i修改前的值 -
++i整个表达式的值是i修改后的值 - 不要依赖
i++和++i在复合表达式中的求值顺序,哪怕看起来“应该”一致
迭代器场景下优先用前置++
对迭代器(尤其是自定义迭代器或 std::list 这类非随机访问容器),it++ 比 ++it 多一次拷贝构造,性能更差。虽然优化器有时能消掉,但习惯上应统一用前置形式。
常见循环写法对比:
// 推荐
for (auto it = vec.begin(); it != vec.end(); ++it) { ... }
// 不推荐(尤其 for 循环里)
for (auto it = vec.begin(); it != vec.end(); it++) { ... }
- 标准库迭代器的
operator++(int)通常比operator++()多一次复制,哪怕只是复制一个指针 - 对于
std::vector::iterator(通常是原生指针),差别可忽略;但对std::map::iterator(封装了红黑树节点指针和平衡信息),开销可能明显 - 统一用
++it可避免未来换容器时的隐性性能退化
bool 类型上使用自增需特别小心
bool 支持 ++,但语义非常反直觉:true++ 结果是 true(因为 true 转为 int 是 1,1+1=2,再转回 bool 是 true);false++ 才变成 true。这和多数人预期的“翻转”完全不同。
实际中几乎不该这么写:
bool flag = true; flag++; // flag 仍是 true —— 不是取反,也不是变 false
-
++flag不等于flag = !flag - 编译器通常会对
bool++发出警告(如 GCC 的-Wbool-operation) - 若真需要切换布尔值,直接写
flag = !flag或flag ^= true


















