c++:迭代器(Iterator)
目录
🚪什么是迭代器?
🔧 迭代器的本质
为什么不用普通数组或下标?
STL容器的迭代器并不是共用一个类型!
迭代器的类型(Iterator Categories)
📦 常见容器的迭代器类型
✅ 迭代器最常用的动作
🚸 初学者推荐写法:使用 auto 和 范围for
🚪什么是迭代器?
迭代器 = “智能指针”,用来一个一个地访问容器中的元素。
你可以想象迭代器像是容器中的“手指”或“光标”:
-
指着某个元素
-
可以向前走(甚至有些还能往后走)
-
能取出或修改元素
示例对比:用数组 vs 用迭代器
int arr[] = {10, 20, 30};
for (int i = 0; i < 3; i++) {std::cout << arr[i];
}
改成 vector
+ 迭代器写法:
std::vector<int> v = {10, 20, 30};
for (std::vector<int>::iterator it = v.begin(); it != v.end(); ++it) {std::cout << *it;
}
-
v.begin()
:返回指向第一个元素的迭代器 -
v.end()
:返回“超出最后一个元素的下一个位置”的迭代器 -
*it
:表示取出当前指向的元素 -
++it
:移动到下一个元素
🔧 迭代器的本质
迭代器是一个类类型(不是基本类型)
虽然它看起来像指针(用 *it
解引用,用 ++it
移动),但它本质上是一个 C++ 类对象,封装了访问容器内部元素的逻辑。
🚩你可以这样理解:
指针 | 迭代器 |
---|---|
访问数组元素 | 访问容器元素 |
用 *ptr 获取值 | 用 *it 获取元素 |
是内建类型 | 是类类型(有重载了 * , ++ , == 等操作符) |
🎓 举个例子看看类型:
std::vector<int> v = {1, 2, 3};
auto it = v.begin();
-
it
实际的类型是std::vector<int>::iterator
-
你可以打印:
std::cout << typeid(it).name() << std::endl;
输出可能是:__gnu_cxx::__normal_iterator<int*, std::vector<int>>
🔍 它是一个内部实现的类,封装了指针行为,但比裸指针更安全、更通用。
为什么不用普通数组或下标?
STL 有很多不同的容器:链表、哈希表、红黑树……
它们里面的元素并不一定支持下标 []
,但每种容器都提供了“迭代器”接口,你可以统一方式遍历。
容器 | 支持下标? | 支持迭代器? |
---|---|---|
vector | ✅ | ✅ |
list | ❌ | ✅ |
set | ❌ | ✅ |
map | ❌ | ✅ |
每个容器都有自己专属的迭代器类型,std::vector<T>::iterator 这种写法是访问这个类型的一种方式。
STL容器的迭代器并不是共用一个类型!
容器类型 | 对应迭代器类型 |
---|---|
std::vector<T> | std::vector<T>::iterator |
std::list<T> | std::list<T>::iterator |
std::set<T> | std::set<T>::iterator |
std::map<K,V> | std::map<K,V>::iterator |
std::vector<int>::iterator it; // vector 的迭代器
std::list<double>::iterator it2; // list 的迭代器
为什么不统一一个 iterator
类型呢?
👉 因为不同容器的内部结构不同(如数组 vs 链表 vs 红黑树),它们的迭代器行为也不同。比如:
-
vector
支持随机访问 (it + 3
) -
list
只支持前后移动(++it
,--it
)
迭代器的类型(Iterator Categories)
STL 根据迭代器的能力,把迭代器分成 五类,从弱到强如下:
类型 | 功能 | 示例容器 |
---|---|---|
InputIterator | 单向只读 | istream_iterator |
OutputIterator | 单向只写 | ostream_iterator |
ForwardIterator | 单向读写 | forward_list |
BidirectionalIterator | 可双向移动 | list , set , map |
RandomAccessIterator | 支持随机访问 it+n , it1-it2 | vector , deque , array |
💡 STL 算法会根据迭代器的种类选择最优的操作方式。
📦 常见容器的迭代器类型
容器 | 支持的迭代器类型 |
---|---|
vector , deque , array | 随机访问迭代器(RandomAccessIterator) |
list | 双向迭代器(BidirectionalIterator) |
forward_list | 单向迭代器(ForwardIterator) |
set , map , multimap , multiset | 双向迭代器(BidirectionalIterator) |
unordered_map , unordered_set | 同样为双向迭代器 |
随机访问迭代器(vector
):
std::vector<int> v = {1, 2, 3, 4};
auto it = v.begin();
std::cout << *(it + 2); // 输出:3
双向迭代器(list
):
std::list<int> l = {1, 2, 3};
auto it = l.end(); --it;
std::cout << *it; // 输出:3
单向迭代器(forward_list
):
std::forward_list<int> fl = {1, 2, 3};
auto it = fl.begin();
++it; // 可以前进,但不能后退
✅ 迭代器最常用的动作
操作 | 含义 |
---|---|
it = container.begin() | 取得起始位置的迭代器 |
it != container.end() | 判断是否结束 |
++it | 移动到下一个元素 |
*it | 获取当前指向的元素 |
std::vector<int> nums = {5, 10, 15};
for (auto it = nums.begin(); it != nums.end(); ++it) {std::cout << *it << " ";
}
操作 | 说明 |
---|---|
--it | 移动到上一个元素(双向或更强迭代器) |
it == it2 , it != it2 | 比较两个迭代器 |
it + n , it - n | 随机访问(仅支持随机访问迭代器) |
std::distance(it1, it2) | 两个迭代器之间的距离 |
std::advance(it, n) | 将迭代器移动 n 个位置(支持不同迭代器类型) |
🚸 初学者推荐写法:使用 auto
和 范围for
从 C++11 开始,可以让写法更简单!
✅ 用 auto
简化迭代器声明:
for (auto it = nums.begin(); it != nums.end(); ++it)
✅ 更简单的方法:范围for(推荐):
范围 for
是 C++11 引入的一种简洁写法,用于遍历容器或数组。它让你不需要写迭代器、不需要用下标,直接访问每一个元素。
for (元素类型 变量名 : 容器) {// 使用变量名代表当前元素
}
传统写法(使用迭代器):
std::vector<int> v = {1, 2, 3, 4};
for (std::vector<int>::iterator it = v.begin(); it != v.end(); ++it) {std::cout << *it << "\n";
}
范围 for
写法:
std::vector<int> v = {1, 2, 3, 4};
for (int x : v) {std::cout << x << "\n";
}
范围 for
背后的原理:
for (auto it = v.begin(); it != v.end(); ++it) {int x = *it;...
}
所以它仍然依赖迭代器,只不过帮你“自动写好了”。