迭代器与范围 for
更新时间:2026-08-27。本文是
languages/cpp/主题入门层第 38 篇(存量stl-containers拆解迁入)。迭代器是"泛化的指针":vector、list、map 都有begin/end,于是同一套遍历代码能跑在所有容器上。范围 for 的底层就是迭代器。本篇把迭代器的分类、失效规则讲透。
本文要回答的问题
- 迭代器是什么?和指针什么关系?
- 范围 for 的底层是怎么工作的?
- 迭代器失效是什么?什么时候踩雷?
一、迭代器:泛化的指针
std::vector<int> v = {3, 1, 2};
for (auto it = v.begin(); it != v.end(); ++it) {
std::cout << *it; // *it 解引用,跟指针一模一样
}迭代器的操作就是指针那套:* 解引用取元素、++ 移到下一个、!= 比较。它把"怎么在容器里移动"抽象成统一接口,于是:
// 这段代码换成 list、deque、map、set 都能跑
for (auto it = c.begin(); it != c.end(); ++it) {
std::cout << *it;
}这就是"泛化"的含义:sort、find 这些算法只认迭代器,不认具体容器(下一篇算法库)。
二、迭代器类型:能力分级
不同容器提供不同能力的迭代器:
| 类别 | 能力 | 容器 |
|---|---|---|
| 随机访问 | it + n、it[n]、it - it | vector、deque、array |
| 双向 | ++、-- | list、map、set |
| 前向 | 只能 ++ | forward_list、unordered_map |
判断标准:std::sort 要求随机访问迭代器(要算中间位置),所以不能直接 sort 一个 list(list 只有双向迭代器,要 l.sort() 成员函数)。算法对迭代器能力有要求,这是常见编译错误来源。
三、范围 for 的底层
for (int x : v) { ... }编译器把它展开成迭代器循环(近似):
for (auto it = v.begin(); it != v.end(); ++it) {
int x = *it; // 拷贝(int 便宜)
...
}所以范围 for 的所有注意点都来自迭代器:
for (int x : v):拷贝每个元素(元素大时浪费,用const auto&);for (auto& x : v):引用,能改原元素;- 遍历时不要增删元素(下一节)。
四、迭代器失效:最隐蔽的雷
容器结构变了,旧迭代器可能"指空"——这叫迭代器失效(invalidation)。经典场景:
std::vector<int> v = {1, 2, 3, 4};
auto it = v.begin(); // 指向 1
v.push_back(5); // 可能触发扩容(第 32 篇)
// *it; // 危险!扩容后内存换了地方,it 悬垂| 操作 | vector | list / map | unordered_map |
|---|---|---|---|
| 插入(可能扩容) | 全部失效 | 不受影响 | 可能全部失效 |
| 删除某个元素 | 被删位置及之后失效 | 只失效被删的 | 只失效被删的 |
push_back 不扩容 | 之前的迭代器仍有效 | 不受影响 | — |
结论:vector 的插入/删除最容易让迭代器失效(扩容全灭、删除后半段灭);list/map 删除只灭自己。遍历中要删元素,别用范围 for,用迭代器循环 + erase 的返回值:
// 删除所有偶数(vector)
for (auto it = v.begin(); it != v.end(); ) {
if (*it % 2 == 0) it = v.erase(it); // erase 返回下一个有效迭代器
else ++it;
}五、和 C 对比
| 对比项 | C | C++ |
|---|---|---|
| 遍历数组 | 指针 p + 长度 | 迭代器 / 范围 for |
| 遍历链表 | 手写 while (p) p = p->next | for (auto x : list) |
| 通用算法 | 每个结构写一套 | 算法只认迭代器 |
| 越界 | 指针满天飞 | 迭代器有 begin/end 约定 |
C 的遍历是"每个容器一套代码":数组用下标、链表用指针、树用递归。C++ 的迭代器把遍历统一了,代价是要学"失效规则"这套纪律。
六、与本站主线衔接
- 算法库
sort/find/count全建立在迭代器上,见 sort / find 等算法库; - 迭代器失效与容器选型深挖,见高手层迭代器失效;
- 下一篇:标准库算法——容器与算法的解耦。
一句话总结
迭代器是"泛化的指针":*、++、!= 统一所有容器的遍历接口,范围 for 底层就是迭代器循环;迭代器分随机访问(vector/deque)、双向(list/map)、前向(unordered)三档,sort 需要随机访问迭代器;vector 插入/删除会让迭代器失效(扩容全灭),遍历中删元素用 it = v.erase(it) 拿下一个有效迭代器。