题目
编写程序,设计具有内部数值状态的迭代计数器 Counter。完整实现前置自增 ++c 与后置自增 c++ 的重载。深入掌握两者的根本区别:①语法签名上,后置通过一个无名字的哑元整型参数 int 区分;②语义上,前置直接修改原对象并返回自身引用(Counter&),后置必须生成临时副本返回旧状态(按值返回 Counter);从汇编与对象生命周期层面证明在 C++ 循环中为什么必须优先推荐使用前置 ++it。
输入格式:无需外部输入。
输出格式:输出前置与后置自增在返回值、原对象状态以及临时副本构造上的对照结果。
数据范围与约定:哑元区分标记、返回值语义与迭代器前置递增零开销准则。
思路与算法
1. 后置重载的哑元 int
为了解决 ++c 和 c++ 在名字查找上的符号冲突,C++ 委员会规定:operator++() 代表前置;而 operator++(int) 代表后置(传入一个编译器生成的哑元 dummy 0)。
2. 性能劣势
后置 operator++(int) 必须先拷贝当前对象构造一个临时对象 Counter old = *this;,然后递增自己,最后把 old 按值返回。在复杂 STL 迭代器(如 map 迭代器)中,这意味着一次多余的拷贝构造与析构。
3. 前置返回引用的左值属性
前置自增返回引用,支持连续自增 ++++c;;而后置返回的是右值临时对象,不支持连续后置自增。
完整程序
#include <iostream>
class Counter {
private:
int value_;
public:
explicit Counter(int val = 0) : value_(val) {}
// 1. 前置自增:无参数,直接就地修改,返回自身引用 (左值,零多余拷贝)
Counter& operator++() {
++value_;
std::cout << " [前置 ++] 就地累加至 " << value_ << ",返回自身引用\n";
return *this;
}
// 2. 后置自增:带哑元 int 参数,必须拷贝旧副本,按值返回 (产生临时对象)
Counter operator++(int) {
Counter temp = *this; // 必须保留并构造旧状态副本
++value_;
std::cout << " [后置 ++] 备份旧状态 " << temp.value_
<< " 并递增至 " << value_ << ",返回旧副本\n";
return temp;
}
int getValue() const { return value_; }
};
int main() {
Counter c1(10);
Counter c2(10);
std::cout << "--- 1. 测试前置自增 ++c1 ---\n";
Counter& ref1 = ++c1;
std::cout << "前置调用返回值: " << ref1.getValue() << ", c1 实际值: " << c1.getValue() << "\n\n";
std::cout << "--- 2. 测试后置自增 c2++ ---\n";
Counter val2 = c2++;
std::cout << "后置调用返回值: " << val2.getValue() << ", c2 实际值: " << c2.getValue() << "\n\n";
std::cout << "结论:在 STL 迭代器遍历循环中,严格推荐使用 ++it(前置),彻底消除多余临时对象的拷贝开销!\n";
return 0;
}运行示例
输出:
--- 1. 测试前置自增 ++c1 ---
[前置 ++] 就地累加至 11,返回自身引用
前置调用返回值: 11, c1 实际值: 11
--- 2. 测试后置自增 c2++ ---
[后置 ++] 备份旧状态 10 并递增至 11,返回旧副本
后置调用返回值: 10, c2 实际值: 11
结论:在 STL 迭代器遍历循环中,严格推荐使用 ++it(前置),彻底消除多余临时对象的拷贝开销!