最稳妥的方式是用 begin() 和 end() 配合 for 循环;下标访问易越界且不通用,而 begin()/end() 是标准容器统一接口,兼容性好、语义清晰,迭代器失效风险更低,循环结束条件应始终用 != 而非其他比较符。

用 begin() 和 end() 配合 for 循环最稳妥
直接用下标访问(vec[i])看似简单,但容易越界且不通用;而 begin()/end() 是所有标准容器的统一接口,兼容性好、语义清晰。迭代器失效风险也比裸指针低。
- 始终用
!=判断循环结束,不是—— 因为 <code>end()返回的是“末尾之后”的位置,对vector虽然也能用,但换成 <code>list就会编译失败 - 如果只读不改,优先用
const_iterator或更简洁的auto+const& - 别把
end()存成变量再循环里反复调用——现代编译器基本会优化掉,但手写时容易误写成i 这种致命错误
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << "\n";
}
for 范围循环(C++11)适合绝大多数读取场景
语法干净、不易出错,底层仍走迭代器,但屏蔽了细节。唯一要注意的是值语义 vs 引用语义的选择。
- 想避免拷贝大对象(比如
std::string或自定义类),必须写const auto&或auto& - 写
auto x会触发拷贝构造,对小类型(int、double)无感,但对大对象就是性能坑 - 不能在循环体内修改容器大小(如
push_back()),否则迭代器可能失效,范围循环会直接 UB
for (const auto& x : vec) {
std::cout << x << "\n"; // 安全、高效
}用 at() 而不是 [] 时才需要边界检查
[] 不做任何检查,越界是未定义行为;at() 会抛 std::out_of_range 异常。但异常开销大,日常遍历不用它。
- 仅在调试阶段或用户输入索引等不可信场景下用
at() - 生产代码中若需索引访问,应先确认
i ,比异常更轻量 -
at()返回的是T&,和[]一样可读可写,但多一层函数调用+分支判断
反向遍历小心 rend() 和 0 的关系
rbegin() 指向最后一个元素,rend() 指向“第一个元素之前”,所以循环条件仍是 != rend(),但初值和步进方向相反。
立即学习“C++免费学习笔记(深入)”;
- 别写
for (auto it = vec.rbegin(); it != vec.rend(); it--)——--在reverse_iterator里是向前走,逻辑反直觉 - 正确写法是
++it,因为reverse_iterator重载了运算符,++实际跳到前一个元素 - 如果用下标反向遍历,注意
size()返回size_t,vec.size() - 1在空 vector 下会绕成极大正数,导致崩溃
for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
std::cout << *it << "\n";
}迭代器的生命期绑定容器,只要没调用 push_back()、erase()、clear() 等可能重分配内存的操作,它就有效。这点比裸指针更可靠,但也意味着你得清楚哪些操作会让它失效。










