用 begin() 和 end() 遍历最安全:绝大多数标准容器(vector、list、map 等)均支持,返回对应迭代器,通用性强、不易出错,自动适配 const 容器及范围 for 底层机制。

用 begin() 和 end() 遍历最安全
绝大多数标准容器(vector、list、map 等)都支持 begin() 和 end(),返回对应类型的迭代器。这是最通用、最不容易出错的方式,且能自动适配 const 容器和范围 for 的底层机制。
常见错误是写成 for (auto it = c.begin(); it —— <code>end() 是尾后迭代器,不能用 比较,必须用 <code>!=;否则在某些 debug 模式下会触发断言失败,或在 release 下越界访问。
- 永远用
!=判断循环结束,不是或 <code> - 如果只读不改,优先用
cbegin()/cend(),避免隐式转 const 迭代器的开销和歧义 - 对
vector这类连续内存容器,operator 虽然可用,但不是所有迭代器都支持(比如 <code>list的迭代器就不支持),所以别依赖它
const_iterator 不等于 “只读”,而是“类型强制”
声明 const vector<int>::iterator it</int> 表示 it 本身不可变(不能指向别处),但依然能通过 *it = 42 修改元素;而 vector<int>::const_iterator it</int> 才真正禁止修改所指元素。
容易踩的坑是混用 iterator 和 const_iterator:比如函数参数是 const vector<int>& v</int>,你却试图用 v.begin() 得到非 const 迭代器——编译直接报错,因为 const 容器只提供 cbegin() 或 begin() 的 const 版本重载。
立即学习“C++免费学习笔记(深入)”;
- 面对 const 容器,必须用
cbegin()/cend(),或确保调用的是 const 成员函数版本 - 不要手动写
vector<int>::const_iterator</int>这种长类型,用auto让编译器推导更安全 - 在模板函数里遍历传入的容器时,统一用
std::begin(c)/std::end(c),它们会自动选对 const/non-const 重载
反向迭代器 reverse_iterator 的 base() 不是“反转索引”
rbegin() 指向最后一个元素,rend() 指向第一个元素前的位置——这没错,但当你拿到一个 reverse_iterator 并想转回普通迭代器时,it.base() 返回的不是同位置的 iterator,而是它“逻辑上后一位”的位置。比如 v.rbegin().base() 实际等于 v.end(),不是 v.begin() + v.size() - 1。
这个偏移容易导致删除或插入操作出错。例如想用反向迭代器找到某个值然后删掉它,直接 v.erase(it.base()) 会删错位置。
- 要删反向迭代器
it指向的元素,得写v.erase((++it).base())或更稳妥地:先转成下标v.size() - 1 - (it - v.rbegin()) -
reverse_iterator的++是往容器开头走,和普通迭代器方向相反,别凭直觉写循环条件 - 除非真需要从后往前处理逻辑,否则优先考虑
std::reverse+ 正向遍历,语义更清晰
unordered_map 迭代器失效规则和 map 完全不同
map 和 set 的迭代器只在对应元素被擦除时失效;而 unordered_map 在 rehash(比如 insert 导致负载因子超限)时,**所有迭代器都会失效**,哪怕你没动那个桶里的元素。
这意味着下面这段代码很危险:
for (auto it = umap.begin(); it != umap.end(); ++it) {
if (some_condition(it->second)) {
umap.erase(it); // ok
} else if (should_insert) {
umap.insert({k, v}); // 可能 rehash → it 失效 → ++it UB!
}
}- 在
unordered_map遍历时,只要可能触发插入/扩容,就别边遍历边改;要么先收集 key,遍历完再批量操作,要么用try_emplace替代insert(不触发 rehash 时更安全) -
reserve(n)可以预分配桶数量,减少运行中 rehash 次数,但不能完全避免 - 如果必须边查边插,用
find()+emplace_hint(),hint 能显著降低冲突概率,但前提是 hint 位置大致接近真实插入点
迭代器本质是泛化的指针,但每种容器的内存布局和实现策略决定了它“像指针”的程度不同。写惯了 vector 就直接套用到 list 或 unordered_map 上,最容易在边界条件和失效规则上栽跟头。










