C++迭代器(Iterator)详解:从原理、使用方法到底层实现全面掌握
1. 什么是迭代器?
在C++ STL(Standard Template Library,标准模板库)中,迭代器(iterator)是连接容器和算法的桥梁。
简单来说:
迭代器是一种类似指针的对象,它可以访问容器中的元素,并且能够遍历容器。
例如:
vector<int> v = {1,2,3,4,5}; for(auto e : v) { cout << e << " "; }这是C++11提供的范围for,本质上编译器帮我们使用了迭代器。
实际上:
for(auto e : v)大致等价于:
auto begin = v.begin(); auto end = v.end(); while(begin != end) { cout << *begin << " "; ++begin; }这里:
begin()返回第一个元素的位置end()返回最后一个元素的下一个位置*begin获取元素++begin移动到下一个元素
2. 为什么需要迭代器?
2.1 不同容器底层结构不同
STL中有很多容器:
| 容器 | 底层结构 |
|---|---|
| vector | 动态数组 |
| list | 双向链表 |
| deque | 双端队列 |
| map | 红黑树 |
| unordered_map | 哈希表 |
访问方式完全不同。
例如:
vector
内存连续:
+---+---+---+---+ |10 |20 |30 |40 | +---+---+---+---+ 地址: 100 104 108 112可以通过指针移动:
ptr++;list
链表:
10 | v 20 | v 30 | v 40节点地址可能完全不连续:
1000 -> 5000 -> 2000无法:
ptr++;因为下一个节点不一定在下一个地址。
2.2 迭代器统一访问方式
有了迭代器:
vector:
vector<int>::iterator it;list:
list<int>::iterator it;map:
map<int,int>::iterator it;虽然底层完全不同,但是遍历方式一样:
for(auto it=container.begin(); it!=container.end(); ++it) { cout<<*it; }这就是STL设计思想:
不关心容器底层,只通过迭代器访问元素。
3. 迭代器的本质
迭代器本质是一种类对象。
例如:
vector<int>::iterator it;实际上:
iterator是vector内部定义的一个类型。
简单模拟:
template<class T> class VectorIterator { public: T* ptr; T& operator*() { return *ptr; } VectorIterator& operator++() { ptr++; return *this; } };这个类实现:
*++!=
于是它就像指针一样使用。
4. 迭代器的基本使用
4.1 begin()
返回第一个元素的位置:
vector<int> v={1,2,3}; auto it=v.begin(); cout<<*it;输出:
1结构:
begin() | v +---+---+---+ | 1 | 2 | 3 | +---+---+---+ ^ it4.2 end()
返回最后一个元素后面的位置:
auto it=v.end();注意:
end不是最后一个元素。
而是:
+---+---+---+----+ | 1 | 2 | 3 | | +---+---+---+----+ ^ end所以:
错误:
cout<<*v.end();这是非法访问。
5. 使用迭代器遍历容器
vector遍历
#include<iostream> #include<vector> using namespace std; int main() { vector<int> v={1,2,3,4}; vector<int>::iterator it=v.begin(); while(it!=v.end()) { cout<<*it<<" "; ++it; } return 0; }输出:
1 2 3 46. auto简化迭代器
以前:
vector<int>::iterator it;非常长。
C++11:
auto it=v.begin();编译器自动推导类型。
推荐:
for(auto it=v.begin(); it!=v.end(); ++it) { cout<<*it; }7. const_iterator
普通迭代器:
iterator可以修改元素。
例如:
vector<int> v={1,2,3}; auto it=v.begin(); *it=100;结果:
100 2 3但是:
如果只想读取:
使用:
const_iterator例如:
vector<int>::const_iterator it; it=v.begin();此时:
*it=100;错误。
原因:
不能通过const迭代器修改数据。
8. reverse_iterator(反向迭代器)
普通迭代器:
方向:
begin() | v 1 2 3 4反向迭代器:
rbegin() 4 3 2 1使用:
vector<int> v={1,2,3,4}; auto it=v.rbegin(); while(it!=v.rend()) { cout<<*it<<" "; ++it; }输出:
4 3 2 19. 五种迭代器类型
STL根据功能不同,把迭代器分为五类。
9.1 输入迭代器(Input Iterator)
特点:
只能读取。
支持:
* ++ == !=例如:
读取文件:
istream_iterator9.2 输出迭代器(Output Iterator)
只能写。
例如:
ostream_iterator用于输出:
copy(v.begin(), v.end(), ostream_iterator<int>(cout," "));9.3 前向迭代器(Forward Iterator)
支持:
读取
写入
++移动
例如:
forward_list9.4 双向迭代器(Bidirectional Iterator)
支持:
向前:
++向后:
--例如:
list map set9.5 随机访问迭代器(Random Access Iterator)
功能最强。
支持:
+ - [] < >例如:
vector:
it+5deque:
it-2迭代器能力关系
Random Access | Bidirectional | Forward Iterator | Input Iterator能力越往上越强。
10. 不同容器迭代器类型
| 容器 | 迭代器类型 |
|---|---|
| vector | 随机访问 |
| deque | 随机访问 |
| array | 随机访问 |
| list | 双向 |
| map | 双向 |
| set | 双向 |
| forward_list | 前向 |
| unordered_map | 前向 |
11. 迭代器失效问题(重点)
这是面试高频问题。
所谓迭代器失效:
迭代器仍然保存地址,但是这个地址已经不是有效元素。
11.1 vector插入导致失效
例如:
vector<int> v={1,2,3}; auto it=v.begin(); v.push_back(4); cout<<*it;可能错误。
原因:
vector扩容:
原空间:
1000: 1 2 3扩容:
5000: 1 2 3 4旧地址释放。
it仍指向1000。
失效。
11.2 vector删除导致失效
vector<int> v={1,2,3}; auto it=v.begin(); v.erase(it);删除后:
2 3原来的it失效。
11.3 list迭代器失效
list:
node1 -> node2 -> node3删除node2:
node1 -> node3只有删除节点的迭代器失效。
其他迭代器仍有效。
12. erase正确使用方式
错误:
for(auto it=v.begin(); it!=v.end(); ++it) { if(*it==3) v.erase(it); }原因:
erase后it失效。
正确:
for(auto it=v.begin(); it!=v.end();) { if(*it==3) { it=v.erase(it); } else { ++it; } }因为:
vector/list的erase会返回删除位置后的迭代器。
13. 迭代器和指针区别
很多人认为:
迭代器就是指针。
不完全正确。
指针:
直接操作地址:
int* p;只能访问内存。
迭代器:
是一种抽象。
可能:
是指针
是类对象
例如:
vector:
iterator ≈ T*list:
iterator: { Node* node; }14. 迭代器和算法
STL算法:
sort find copy reverse都使用迭代器。
例如:
排序:
vector<int> v={3,1,2}; sort(v.begin(), v.end());sort不知道:
vector是什么
数据在哪里
它只认识:
begin() end() ++ *15. 迭代器底层思想
STL采用:
泛型编程
算法:
template<class Iterator> void sort(Iterator first, Iterator last)不关心类型。
只要求:
这个Iterator满足随机访问能力。
这就是:
面向接口编程。
16. 常用迭代器接口总结
| 函数 | 作用 |
|---|---|
| begin() | 返回头迭代器 |
| end() | 返回尾后迭代器 |
| rbegin() | 返回反向头 |
| rend() | 返回反向尾 |
| cbegin() | const开始 |
| cend() | const结束 |
17. 迭代器总结
什么是迭代器?
迭代器是STL中用于访问容器元素的一种对象,本质是对指针的封装。
为什么需要迭代器?
因为:
不同容器底层不同
统一算法访问方式
核心使用:
auto it=container.begin(); while(it!=container.end()) { cout<<*it; ++it; }必须掌握:
begin/end
iterator
const_iterator
reverse_iterator
五种迭代器分类
迭代器失效
STL算法与迭代器关系
