迭代器是 C++ 中用于 遍历容器(如数组、链表、哈希表等) 的工具,它像 智能指针 一样,可以逐个访问容器中的元素,而不用关心容器的内部实现。以下是迭代器的全面教程:
1. 为什么需要迭代器?
假设你要遍历一个数组和一个链表,传统方式需要不同的代码:
// 遍历数组
int arr[5] = {1, 2, 3, 4, 5};
for (int i = 0; i < 5; i++) { /* 用下标访问 arr[i] */ }
// 遍历链表
struct Node { int data; Node* next; };
Node* head = /* 链表头 */;
for (Node* p = head; p != nullptr; p = p->next) { /* 用指针访问 p->data */ }
问题:两种方式完全不同,代码难以复用。
迭代器 提供 统一的访问方式,无论容器是数组、链表还是哈希表,都可以用相同的方法遍历:
// 统一用迭代器遍历
auto it = container.begin(); // 获取开始位置
while (it != container.end()) { // 判断是否到末尾
std::cout << *it; // 访问当前元素
++it; // 移动到下一个元素
}
2. 迭代器的基本使用
(1) 获取迭代器
begin()→ 指向 第一个元素 的迭代器end()→ 指向 最后一个元素的下一个位置(类似“结束标记”)
#include <vector>
#include <iostream>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
// 获取迭代器
auto it = vec.begin(); // 指向 1
auto end = vec.end(); // 指向 5 的后面(不是 5!)
// 遍历
while (it != end) {
std::cout << *it << " "; // 解引用,访问当前元素
++it; // 移动到下一个元素
}
return 0;
}
输出:1 2 3 4 5
(2) 范围-based for 循环(推荐)
C++11 后,可以用更简单的语法遍历:
for (int val : vec) { // 自动用迭代器遍历
std::cout << val << " ";
}
等价于:
for (auto it = vec.begin(); it != vec.end(); ++it) {
int val = *it;
std::cout << val << " ";
}
3. 迭代器的核心操作
| 操作 | 说明 | 示例 |
|---|---|---|
*it | 解引用,访问当前元素 | *it = 10;(修改元素) |
++it | 移动到下一个元素 | it++; 或 ++it; |
it != end | 判断是否遍历结束 | while (it != vec.end()) |
it1 == it2 | 比较两个迭代器 | if (it1 == it2) |
示例:修改容器元素
std::vector<int> vec = {1, 2, 3};
for (auto it = vec.begin(); it != vec.end(); ++it) {
*it *= 2; // 每个元素乘以 2
}
// vec 变成 {2, 4, 6}
4. 迭代器与不同容器
(1) 数组 (std::vector, std::array)
std::vector<int> vec = {1, 2, 3};
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
(2) 链表 (std::list)
std::list<int> lst = {1, 2, 3};
for (auto it = lst.begin(); it != lst.end(); ++it) {
std::cout << *it << " ";
}
(3) 哈希表 (std::unordered_map)
std::unordered_map<std::string, int> ageMap = {{"Alice", 25}, {"Bob", 30}};
for (auto it = ageMap.begin(); it != ageMap.end(); ++it) {
std::cout << it->first << " -> " << it->second << std::endl;
// it->first 是键,it->second 是值
}
(4) 集合 (std::set)
std::set<int> s = {3, 1, 2};
for (auto it = s.begin(); it != s.end(); ++it) {
std::cout << *it << " "; // 输出:1 2 3(自动排序)
}
5. 注意事项
(1) end() 不能解引用
auto it = vec.end();
// *it; // 错误!崩溃!
end() 只是 结束标记,不代表任何实际元素。
(2) 迭代器可能失效
- 如果对容器 插入/删除元素,某些迭代器可能失效(不能再使用)。
- 安全做法:插入/删除后,重新获取迭代器。
(3) 不同容器的迭代器功能不同
| 容器 | 支持的迭代器操作 |
|---|---|
vector、array | ++、--、+n、-n、[] |
list、map、set | ++、--(不支持随机访问) |
unordered_map | ++(不支持 -- 和随机访问) |
6. 总结
- 迭代器 是遍历容器的 通用工具,让代码更简洁、更灵活。
- 核心操作:
*it(访问)、++it(移动)、it != end(判断结束)。 - 推荐 使用范围-based for 循环(
for (auto x : container))。 - 注意
end()不能解引用,迭代器可能失效。
一句话记忆:迭代器就像 指针 + 规则,让你用统一的方式遍历各种容器! 🚀
📌 1. 迭代器的核心概念
什么是迭代器?
- 迭代器是 类指针对象,可以遍历容器中的元素
- 支持
*it(解引用)、++it(前进)、==/!=(比较)等操作 - 不依赖容器类型:同一算法可以作用于不同容器(如
vector和list)
类比:
- 像 游标(如 SQL 游标)、书签、指针 的结合体
📌 2. 迭代器类别(5 种)
C++ 标准定义了 5 种迭代器类别,按功能从弱到强排列:
| 类别 | 支持的操作 | 典型容器 | 示例 |
|---|---|---|---|
| 输入迭代器 | 只读,单向(++) | istream_iterator | 读取文件 |
| 输出迭代器 | 只写,单向(++) | ostream_iterator | 写入文件 |
| 前向迭代器 | 读写,单向(++) | forward_list、unordered_map | 哈希表 |
| 双向迭代器 | 读写,双向(++/--) | list、map、set | 双向链表 |
| 随机访问迭代器 | 读写,随机(+n/-n/[]) | vector、deque、array | 数组 |
📊 能力对比
| 操作 | 输入 | 输出 | 前向 | 双向 | 随机访问 |
|---|---|---|---|---|---|
*it | ✅ 读 | ✅ 写 | ✅ 读/写 | ✅ 读/写 | ✅ 读/写 |
++it | ✅ | ✅ | ✅ | ✅ | ✅ |
--it | ❌ | ❌ | ❌ | ✅ | ✅ |
it + n | ❌ | ❌ | ❌ | ❌ | ✅ |
it[n] | ❌ | ❌ | ❌ | ❌ | ✅ |
it1 < it2 | ❌ | ❌ | ❌ | ❌ | ✅ |
📌 3. 基本语法和操作
1. 获取迭代器
#include <vector>
std::vector<int> vec = {1, 2, 3, 4, 5};
// begin() -> 第一个元素
auto itBegin = vec.begin();
// end() -> 末尾标记(不是最后一个元素!)
auto itEnd = vec.end();
2. 迭代器操作
// 解引用(访问元素)
int value = *itBegin; // value = 1
// 前进到下一个元素
++itBegin; // 或 itBegin++
*itBegin = 10; // 修改元素:vec[1] = 10
// 比较
if (itBegin != itEnd) { /* ... */ }
// 随机访问(仅随机访问迭代器)
auto it3 = vec.begin() + 3; // 指向第 4 个元素
int val = it3[2]; // 等价于 *(it3 + 2),val = 5
3. 遍历容器
// 传统 for 循环
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
// 范围-based for 循环(C++11+,推荐)
for (const auto& val : vec) {
std::cout << val << " ";
}
📌 4. 迭代器失效(Critical!)
什么情况下迭代器失效?
| 容器 | 操作 | 迭代器影响 |
|---|---|---|
vector | 插入/删除 | 所有迭代器可能失效(内存重新分配) |
deque | 首尾插入 | 首尾迭代器失效 |
list/forward_list | 插入/删除 | 仅被删除的迭代器失效 |
map/set | 插入/删除 | 仅被删除的迭代器失效 |
unordered_map | 插入 | 所有迭代器可能失效(重新哈希) |
安全实践
// ❌ 危险:删除后继续使用迭代器
auto it = vec.begin();
vec.erase(it);
// *it = 100; // 未定义行为!
// ✅ 安全:erase 返回下一个有效迭代器
auto it = vec.begin();
it = vec.erase(it); // 返回下一个元素
*it = 100; // 安全
📌 5. 迭代器适配器(Iterator Adapters)
1. 反向迭代器(Reverse Iterator)
#include <vector>
std::vector<int> vec = {1, 2, 3, 4, 5};
// rbegin() -> 最后一个元素,rend() -> 第一个元素的前一个位置
for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
std::cout << *it << " "; // 输出:5 4 3 2 1
}
2. 插入迭代器(Insert Iterator)
#include <iterator>
#include <vector>
std::vector<int> vec = {1, 2, 3};
std::vector<int> vec2;
// back_inserter:插入到末尾
std::copy(vec.begin(), vec.end(), std::back_inserter(vec2));
// front_inserter:插入到开头(仅支持 list/deque)
std::list<int> lst;
std::copy(vec.begin(), vec.end(), std::front_inserter(lst));
// inserter:插入到指定位置
auto it = vec2.begin() + 1;
std::copy(vec.begin(), vec.end(), std::inserter(vec2, it));
3. 流迭代器(Stream Iterator)
#include <iterator>
#include <iostream>
#include <vector>
// 从 cin 读取整数
std::istream_iterator<int> inIt(std::cin), endIt;
std::vector<int> vec(inIt, endIt);
// 输出到 cout
std::ostream_iterator<int> outIt(std::cout, " ");
std::copy(vec.begin(), vec.end(), outIt);
📌 6. 自定义迭代器
示例:实现一个简单的动态数组迭代器
template <typename T>
class SimpleVector {
private:
T* data;
size_t size;
public:
// 迭代器类
class Iterator {
private:
T* ptr;
public:
Iterator(T* p) : ptr(p) {}
// 必须实现的操作
T& operator*() { return *ptr; }
Iterator& operator++() { ++ptr; return *this; }
bool operator!=(const Iterator& other) const { return ptr != other.ptr; }
};
// 容器接口
Iterator begin() { return Iterator(data); }
Iterator end() { return Iterator(data + size); }
// 构造函数等...
SimpleVector(size_t s) : size(s) { data = new T[s]; }
~SimpleVector() { delete[] data; }
};
// 使用
SimpleVector<int> vec(5);
for (auto& val : vec) {
val = 42; // 通过迭代器访问
}
📌 7. STL 算法与迭代器
1. 查找
#include <algorithm>
auto it = std::find(vec.begin(), vec.end(), 3);
if (it != vec.end()) {
std::cout << "Found at position: " << (it - vec.begin()) << std::endl;
}
2. 排序
std::sort(vec.begin(), vec.end()); // 仅随机访问迭代器
3. 变换
std::transform(vec.begin(), vec.end(), vec.begin(), [](int x) { return x * 2; });
4. 删除
auto newEnd = std::remove(vec.begin(), vec.end(), 3);
vec.erase(newEnd, vec.end());
📌 8. 最佳实践
✅ 推荐
- 优先使用范围-based for 循环(C++11+):
for (const auto& val : container) { /* ... */ } - 避免保存长期迭代器(可能失效)
- 使用
auto简化迭代器声明:auto it = container.begin(); // 比 std::vector<int>::iterator 更简洁 - const 正确性:
const std::vector<int>& vec = getConstVec(); auto it = vec.cbegin(); // 显式 const 迭代器
🚫 避免
- 解引用
end()迭代器:auto it = vec.end(); // *it; // 崩溃! - 比较不同容器的迭代器:
auto it1 = vec1.begin(); auto it2 = vec2.begin(); // if (it1 == it2) {} // 未定义行为!
📚 总结
| 主题 | 关键点 |
|---|---|
| 核心作用 | 统一访问容器,连接算法 |
| 5 种类别 | 输入、输出、前向、双向、随机访问 |
| 失效问题 | 插入/删除后需重新获取 |
| 适配器 | 反向、插入、流迭代器 |
| 最佳实践 | 范围-for、auto、const 正确性 |
迭代器是 C++ 泛型编程 的基石,理解它才能高效使用 STL! 🚀