C++ STL迭代器:连接算法和容器的桥梁
迭代器是STL中的一个重要组件。它就像容器的 "导航员",无论你面对的是 vector、list 还是 map,都能带你精准遍历每一个元素。学习STL就必须要掌握迭代器的基本用法,一起来看看
迭代器到底是个啥?
打个比方,你去图书馆找书,不需要知道书在仓库的具体位置,只需通过书架编号和检索系统就能找到目标。迭代器就扮演着类似的角色 —— 它让我们无需了解容器内部存储结构,就能访问容器中的元素
迭代器本质是一种泛化的指针抽象,它定义了对容器元素的访问和遍历操作。通过迭代器实现了一套访问容器元素的通用机制
为什么需要迭代器?如果你写过C语言代码,对于数组、链表、树,图这几种数据结构,都各自有一套遍历的方法。而且对于越复杂的数据结构,遍历算法也会越复杂
而迭代器的出现,让我们可以用同一种逻辑遍历所有容器,无论你面对的是连续内存的vector、双向链表list,还是其他复杂容器,迭代器都能屏蔽底层实现差异,让你用同样的++、*操作轻松游走于元素之间
这也是 STL(标准模板库)的核心设计思想 ——将数据存储和算法分离,这也正是C++泛型编程的精髓所在。
迭代器的核心作用:连接容器与算法的桥梁
迭代器最关键的价值在于标准化遍历逻辑,实现了容器与算法的解耦设计。STL算法从不直接操作容器,而是通过迭代器间接访问元素——这意味着同一个sort排序算法,既能处理vector的连续内存数据,也能处理list的链表节点,甚至是自定义容器
其核心功能可概括为三点:
-
遍历容器
支持前进(
++it)、后退(--it,部分迭代器)或随机访问,轻松遍历元素序列; -
统一接口
所有STL容器都提供
begin()(起始迭代器)和end()(结束迭代器),构成算法操作的标准区间[begin, end); -
泛化复用
通用算法(如for_each、find)通过迭代器接口,可跨容器类型复用,大幅降低开发成
看看一个简单示例
#include <iostream>#include <vector>int main(){std::vector<int> nums = {1, 2, 3, 4, 5};// 使用迭代器遍历vectorfor (auto it = nums.begin(); it != nums.end(); ++it) {std::cout << *it << " "; // 像指针一样使用*访问元素}// 输出:1 2 3 4 5return 0;}
上面的代码展示了迭代器的基本用法,可以看到和指针很类似。但迭代器比指针更强大、更安全,它会根据容器类型自动适配最佳的访问方式。
迭代器与指针的区别
从上面的例子可以看到,迭代器与指针的操作是非常类似,都可以使用*、++,--等操作。但这两者本质上是不同的。我们从两个维度,看看两者的本质区别:
1. 本质定位:“抽象接口” VS “内存地址”
- 指针
是底层硬件的直接映射,本质是 “存储内存地址的变量”,它只知道 “地址”,不知道 “地址背后的数据属于哪个容器”。
- 迭代器
是 “容器专属的访问接口”,它不仅包含地址信息,还知道自己属于哪种容器、该如何移动(比如 list 迭代器要找链表节点的 next 指针,vector 迭代器直接跳内存)。
示例:用指针遍历链表会直接崩溃
#include <list>#include <iostream>int main(){std::list<int> nums = {1, 2, 3};int* p = &nums.front(); // 取第一个元素的地址(指针)// 错误!list元素不连续,p+1会指向随机内存for (int i = 0; i < 3; ++i) {std::cout << *(p + i) << " "; // 未定义行为,大概率崩溃}// 正确!迭代器知道怎么遍历链表for (auto it = nums.begin(); it != nums.end(); ++it) {std::cout << *it << " "; // 输出:1 2 3}return 0;}
2. 类型安全:“容器专属” VS “任意访问”
- 指针
没有类型绑定,只要强制转换,就能访问任意内存(比如把 int 指针转 char 指针),容易引发内存越界。
- 迭代器
和容器强绑定,比如std::list<int>::iterator只能访问 list 的 int 元素,无法访问 vector 的元素,编译器会帮你拦截错误。
示例:
std::vector<int> vec = {10, 20};std::list<int> lst = {30, 40};// 指针:强制转换后能“跨容器”访问(危险!)int* p_vec = &vec[0];int* p_lst = (int*)&lst.front(); // 强制转换,编译器不报错std::cout << *(p_vec + 1); // 20(侥幸对)std::cout << *(p_lst + 1); // 随机值(崩溃风险)// 迭代器:跨容器赋值直接报错(安全!)std::vector<int>::iterator it_vec = vec.begin();std::list<int>::iterator it_lst = lst.begin();// it_vec = it_lst; // 编译错误:类型不匹配,无法赋值
迭代器的分类
不同容器的底层结构决定了它们能提供的迭代器类型,迭代器一共分为 5 类,如下表
|
迭代器类型 |
功能描述 |
支持的操作 |
典型适用容器 |
|
输入迭代器 |
只能读取,单向移动 |
++, *, ->, ==, != |
istream |
|
输出迭代器 |
只能写入,单向移动 |
++, * |
ostream |
|
前向迭代器 |
可读写,单向移动 |
输入、输出迭代器的所有操作 |
forward_list, unordered_set |
|
双向迭代器 |
可读写,双向移动 |
前向迭代器所有操作 ,-- |
list, set, map |
|
随机访问迭代器 |
可读写,随机访问 |
双向迭代器所有操作,[], +, -, +=, -, <,> 等 |
vector, deque, array |
在实际开发中,选择合适容器-迭代器组合能发挥最大效力:
- 当需要快速随机访问
(比如通过索引获取元素):选
vector/deque的随机访问迭代器,它们能在常数时间内“跳”到目标位置,就像电梯直达楼层。 - 当需要频繁插入删除
(尤其是中间位置):选
list的双向迭代器,链表结构让插入删除只需调整指针,不会像vector那样导致大量元素移动。 - 当处理数据流
用
istream_iterator/ostream_iterator,它们就像水管的阀门,按顺序读写数据,不支持回溯。
迭代器应用于算法时,必须要根据算法选择合适的迭代器。比如用 std::ranges::sort() 排序时,若传入 std::forward_list(前向迭代器),编译器会直接报错——因为排序算法需要随机访问迭代器才能交换任意位置元素。记住:容器的迭代器类型是“天生的”,不会因算法需求而改变,选容器时就要考虑后续要用什么算法。
常用的算法对应的迭代器:
|
算法类型 |
最低迭代器要求 |
适用容器示例 |
|---|---|---|
|
std::find |
输入迭代器 |
list、vector、istream |
|
std::reverse |
双向迭代器 |
list、vector |
|
std::sort |
随机访问迭代器 |
vector、deque |
|
std::transform |
输入+输出迭代器 |
所有容器+插入迭代器 |
迭代器的的基本操作
-
获取迭代器
std::vector<int> vec = {10, 20, 30};auto it_begin = vec.begin(); // 指向第一个元素的迭代器auto it_end = vec.end(); // 指向最后一个元素后面的迭代器(哨兵位置)auto cit_begin = vec.cbegin(); // const迭代器,不能通过它修改元素
-
迭代器移动
std::vector<int> vec = {1, 2, 3, 4, 5};auto it = vec.begin();++it; // 移动到下一个元素(推荐,效率更高)it++; // 返回当前元素,然后移动到下一个元素it += 3; // 随机访问迭代器支持直接跳3步--it; // 双向及随机迭代器支持
-
访问元素
std::vector<int> vec = {1, 2, 3};auto it = vec.begin();*it = 10; // 修改元素值std::cout << *it; // 读取元素值std::vector<std::string> strs = {"hello", "world"};auto sit = strs.begin();std::cout << sit->size(); // 访问成员,等价于(*sit).size()
遍历容器的三种方式
传统 for 循环:
std::vector<int> nums = {1, 2, 3, 4, 5};for (auto it = nums.begin(); it != nums.end(); ++it) {std::cout << *it << " ";}
范围 for 循环(本质还是用迭代器):
// 编译器会自动转换为迭代器形式for (int num : nums) {std::cout << num << " ";}
反向遍历:
// rbegin()指向最后一个元素,rend()指向第一个元素前面for (auto it = nums.rbegin(); it != nums.rend(); ++it) {std::cout << *it << " "; // 输出:5 4 3 2 1}
注意上面虽然是从后向前遍历,但因为是反向迭代器,还是要用++,而不是--
迭代器结合算法使用
迭代器和 STL 算法经常会搭配使用,比如查找元素,排序等:
#include <algorithm> // 包含算法库std::vector<int> nums = {10, 20, 30, 40};// 查找值为30的元素auto it = std::find(nums.begin(), nums.end(), 30);if (it != nums.end()) {std::cout << "找到元素:" << *it << ",位置:" << it - nums.begin();} else {std::cout << "未找到元素";}// 按降序排序std::sort(nums.begin(), nums.end(),descending);for(auto i:nums) {std::cout << i; //输出40,30,20,10}
迭代器使用的易错点
迭代器虽好用,但一不小心就会掉坑里。这些易错点必须牢记:
-
迭代器失效问题(最常见!)
当容器发生变化时,迭代器可能会失效,继续使用会导致未定义行为。
例如vector 的插入 / 删除陷阱
std::vector<int> vec = {1, 2, 3, 4};auto it = vec.begin() + 2; // 指向3// 插入元素后,原迭代器可能失效vec.insert(it, 100);// *it; // 危险!可能已经失效// 正确做法:使用insert的返回值it = vec.insert(it, 100); // insert返回新插入元素的迭代器// 删除元素同样危险vec.erase(it);// *it; // 已失效!// 正确做法it = vec.erase(it); // erase返回下一个有效元素的迭代器
为什么迭代器会失效?
vector 的底层是动态分配的数组,插入 / 删除操作可能导致内存重新分配,原迭代器指向的内存地址已经无效。
不同容器的失效规则:
-
vector:插入可能导致所有迭代器失效;删除导致删除点及之后的迭代器失效
-
list:插入 / 删除只影响被删除元素的迭代器,其他不受影响
-
map/set:插入不失效;删除只影响被删除元素的迭代器
-
unordered_*:插入可能导致重哈希,所有迭代器失效
-
越界访问
std::vector<int> vec = {1, 2, 3};auto it = vec.begin();// 错误:移动到了end()之后while (it <= vec.end()) { // 应该用 != 而不是 <=std::cout << *it++;}
注意end () 返回的是 “最后一个元素的下一个位置”,不能解引用(*it)。判断迭代器是否到达末尾应该用!=,而不是<=,因为不是所有迭代器都支持<比较。
-
const 迭代器的使用
const迭代器表示只读迭代器,也就是不能通过它来对元素进行修改。对于只需要读访问元素的场景,使用const迭代器可以避免意外的修改,例如:
std::vector<int> vec = {1, 2, 3};auto it = vec.cbegin(); // const迭代器// *it = 10; // 错误!const迭代器不能修改元素// 正确:需要修改元素用非const迭代器auto mut_it = vec.begin();*mut_it = 10; // 没问题
在C++11中,cbegin()和cend()返回的就是 const 迭代器,即使容器本身不是 const 的,也能保证不修改元素。
-
迭代器的生命周期
迭代器的生命周期不能超过它所指向的容器,容器销毁后,迭代器也随之失效。
std::vector<int> get_vector() {return {1, 2, 3};}// 危险!auto it = get_vector().begin();// *it; // 临时vector已销毁,迭代器失效
总结:迭代器的使用注意事项
-
优先使用 auto 声明迭代器
减少代码冗余,避免类型错误
// 推荐auto it = vec.begin();// 不推荐std::vector<int>::iterator it = vec.begin();
-
优先使用迭代器而非下标
除非确定是随机访问容器,迭代器的通用性更好
// 通用写法,适用于所有容器for (auto it = container.begin(); it != container.end(); ++it)// 只适用于随机访问容器,通过下标访问for (size_t i = 0; i < container.size(); ++i)
-
处理插入删除时,避免迭代器失效
// 正确的删除方式for (auto it = vec.begin(); it != vec.end(); ) {if (*it % 2 == 0) {it = vec.erase(it); // 用返回值更新迭代器} else {++it;}}
-
需要只读访问时,使用 const 迭代器
提高代码可读性和安全性
for (auto it = vec.cbegin(); it != vec.cend(); ++it) {*it = 2; // 编译报错,不可修改}
-
不要把迭代器当指针用
避免用指针的思维操作迭代器(比如&(*it)取地址后长期持有),防止容器修改后内存失效
迭代器看似简单,但真正用好需要不断实践。这里也只是列举了迭代器的一些基本用法,还有一些进阶用法比如自定义迭代器等,等以后深入学习了再来总结
迭代器是STL中的一个重要组件。它就像容器的 "导航员",无论你面对的是 vector、list 还是 map,都能带你精准遍历每一个元素。学习STL就必须要掌握迭代器的基本用法,一起来看看
迭代器到底是个啥?
打个比方,你去图书馆找书,不需要知道书在仓库的具体位置,只需通过书架编号和检索系统就能找到目标。迭代器就扮演着类似的角色 —— 它让我们无需了解容器内部存储结构,就能访问容器中的元素
迭代器本质是一种泛化的指针抽象,它定义了对容器元素的访问和遍历操作。通过迭代器实现了一套访问容器元素的通用机制
为什么需要迭代器?如果你写过C语言代码,对于数组、链表、树,图这几种数据结构,都各自有一套遍历的方法。而且对于越复杂的数据结构,遍历算法也会越复杂
而迭代器的出现,让我们可以用同一种逻辑遍历所有容器,无论你面对的是连续内存的vector、双向链表list,还是其他复杂容器,迭代器都能屏蔽底层实现差异,让你用同样的++、*操作轻松游走于元素之间
这也是 STL(标准模板库)的核心设计思想 ——将数据存储和算法分离,这也正是C++泛型编程的精髓所在。
迭代器的核心作用:连接容器与算法的桥梁
迭代器最关键的价值在于标准化遍历逻辑,实现了容器与算法的解耦设计。STL算法从不直接操作容器,而是通过迭代器间接访问元素——这意味着同一个sort排序算法,既能处理vector的连续内存数据,也能处理list的链表节点,甚至是自定义容器
其核心功能可概括为三点:
-
遍历容器
支持前进(
++it)、后退(--it,部分迭代器)或随机访问,轻松遍历元素序列; -
统一接口
所有STL容器都提供
begin()(起始迭代器)和end()(结束迭代器),构成算法操作的标准区间[begin, end); -
泛化复用
通用算法(如for_each、find)通过迭代器接口,可跨容器类型复用,大幅降低开发成
看看一个简单示例
#include <iostream>#include <vector>int main(){std::vector<int> nums = {1, 2, 3, 4, 5};// 使用迭代器遍历vectorfor (auto it = nums.begin(); it != nums.end(); ++it) {std::cout << *it << " "; // 像指针一样使用*访问元素}// 输出:1 2 3 4 5return 0;}
上面的代码展示了迭代器的基本用法,可以看到和指针很类似。但迭代器比指针更强大、更安全,它会根据容器类型自动适配最佳的访问方式。
迭代器与指针的区别
从上面的例子可以看到,迭代器与指针的操作是非常类似,都可以使用*、++,--等操作。但这两者本质上是不同的。我们从两个维度,看看两者的本质区别:
1. 本质定位:“抽象接口” VS “内存地址”
- 指针
是底层硬件的直接映射,本质是 “存储内存地址的变量”,它只知道 “地址”,不知道 “地址背后的数据属于哪个容器”。
- 迭代器
是 “容器专属的访问接口”,它不仅包含地址信息,还知道自己属于哪种容器、该如何移动(比如 list 迭代器要找链表节点的 next 指针,vector 迭代器直接跳内存)。
示例:用指针遍历链表会直接崩溃
#include <list>#include <iostream>int main(){std::list<int> nums = {1, 2, 3};int* p = &nums.front(); // 取第一个元素的地址(指针)// 错误!list元素不连续,p+1会指向随机内存for (int i = 0; i < 3; ++i) {std::cout << *(p + i) << " "; // 未定义行为,大概率崩溃}// 正确!迭代器知道怎么遍历链表for (auto it = nums.begin(); it != nums.end(); ++it) {std::cout << *it << " "; // 输出:1 2 3}return 0;}
2. 类型安全:“容器专属” VS “任意访问”
- 指针
没有类型绑定,只要强制转换,就能访问任意内存(比如把 int 指针转 char 指针),容易引发内存越界。
- 迭代器
和容器强绑定,比如std::list<int>::iterator只能访问 list 的 int 元素,无法访问 vector 的元素,编译器会帮你拦截错误。
示例:
std::vector<int> vec = {10, 20};std::list<int> lst = {30, 40};// 指针:强制转换后能“跨容器”访问(危险!)int* p_vec = &vec[0];int* p_lst = (int*)&lst.front(); // 强制转换,编译器不报错std::cout << *(p_vec + 1); // 20(侥幸对)std::cout << *(p_lst + 1); // 随机值(崩溃风险)// 迭代器:跨容器赋值直接报错(安全!)std::vector<int>::iterator it_vec = vec.begin();std::list<int>::iterator it_lst = lst.begin();// it_vec = it_lst; // 编译错误:类型不匹配,无法赋值
迭代器的分类
不同容器的底层结构决定了它们能提供的迭代器类型,迭代器一共分为 5 类,如下表
|
迭代器类型 |
功能描述 |
支持的操作 |
典型适用容器 |
|
输入迭代器 |
只能读取,单向移动 |
++, *, ->, ==, != |
istream |
|
输出迭代器 |
只能写入,单向移动 |
++, * |
ostream |
|
前向迭代器 |
可读写,单向移动 |
输入、输出迭代器的所有操作 |
forward_list, unordered_set |
|
双向迭代器 |
可读写,双向移动 |
前向迭代器所有操作 ,-- |
list, set, map |
|
随机访问迭代器 |
可读写,随机访问 |
双向迭代器所有操作,[], +, -, +=, -, <,> 等 |
vector, deque, array |
在实际开发中,选择合适容器-迭代器组合能发挥最大效力:
- 当需要快速随机访问
(比如通过索引获取元素):选
vector/deque的随机访问迭代器,它们能在常数时间内“跳”到目标位置,就像电梯直达楼层。 - 当需要频繁插入删除
(尤其是中间位置):选
list的双向迭代器,链表结构让插入删除只需调整指针,不会像vector那样导致大量元素移动。 - 当处理数据流
用
istream_iterator/ostream_iterator,它们就像水管的阀门,按顺序读写数据,不支持回溯。
迭代器应用于算法时,必须要根据算法选择合适的迭代器。比如用 std::ranges::sort() 排序时,若传入 std::forward_list(前向迭代器),编译器会直接报错——因为排序算法需要随机访问迭代器才能交换任意位置元素。记住:容器的迭代器类型是“天生的”,不会因算法需求而改变,选容器时就要考虑后续要用什么算法。
常用的算法对应的迭代器:
|
算法类型 |
最低迭代器要求 |
适用容器示例 |
|---|---|---|
|
std::find |
输入迭代器 |
list、vector、istream |
|
std::reverse |
双向迭代器 |
list、vector |
|
std::sort |
随机访问迭代器 |
vector、deque |
|
std::transform |
输入+输出迭代器 |
所有容器+插入迭代器 |
迭代器的的基本操作
-
获取迭代器
std::vector<int> vec = {10, 20, 30};auto it_begin = vec.begin(); // 指向第一个元素的迭代器auto it_end = vec.end(); // 指向最后一个元素后面的迭代器(哨兵位置)auto cit_begin = vec.cbegin(); // const迭代器,不能通过它修改元素
-
迭代器移动
std::vector<int> vec = {1, 2, 3, 4, 5};auto it = vec.begin();++it; // 移动到下一个元素(推荐,效率更高)it++; // 返回当前元素,然后移动到下一个元素it += 3; // 随机访问迭代器支持直接跳3步--it; // 双向及随机迭代器支持
-
访问元素
std::vector<int> vec = {1, 2, 3};auto it = vec.begin();*it = 10; // 修改元素值std::cout << *it; // 读取元素值std::vector<std::string> strs = {"hello", "world"};auto sit = strs.begin();std::cout << sit->size(); // 访问成员,等价于(*sit).size()
遍历容器的三种方式
传统 for 循环:
std::vector<int> nums = {1, 2, 3, 4, 5};for (auto it = nums.begin(); it != nums.end(); ++it) {std::cout << *it << " ";}
范围 for 循环(本质还是用迭代器):
// 编译器会自动转换为迭代器形式for (int num : nums) {std::cout << num << " ";}
反向遍历:
// rbegin()指向最后一个元素,rend()指向第一个元素前面for (auto it = nums.rbegin(); it != nums.rend(); ++it) {std::cout << *it << " "; // 输出:5 4 3 2 1}
注意上面虽然是从后向前遍历,但因为是反向迭代器,还是要用++,而不是--
迭代器结合算法使用
迭代器和 STL 算法经常会搭配使用,比如查找元素,排序等:
#include <algorithm> // 包含算法库std::vector<int> nums = {10, 20, 30, 40};// 查找值为30的元素auto it = std::find(nums.begin(), nums.end(), 30);if (it != nums.end()) {std::cout << "找到元素:" << *it << ",位置:" << it - nums.begin();} else {std::cout << "未找到元素";}// 按降序排序std::sort(nums.begin(), nums.end(),descending);for(auto i:nums) {std::cout << i; //输出40,30,20,10}
迭代器使用的易错点
迭代器虽好用,但一不小心就会掉坑里。这些易错点必须牢记:
-
迭代器失效问题(最常见!)
当容器发生变化时,迭代器可能会失效,继续使用会导致未定义行为。
例如vector 的插入 / 删除陷阱
std::vector<int> vec = {1, 2, 3, 4};auto it = vec.begin() + 2; // 指向3// 插入元素后,原迭代器可能失效vec.insert(it, 100);// *it; // 危险!可能已经失效// 正确做法:使用insert的返回值it = vec.insert(it, 100); // insert返回新插入元素的迭代器// 删除元素同样危险vec.erase(it);// *it; // 已失效!// 正确做法it = vec.erase(it); // erase返回下一个有效元素的迭代器
为什么迭代器会失效?
vector 的底层是动态分配的数组,插入 / 删除操作可能导致内存重新分配,原迭代器指向的内存地址已经无效。
不同容器的失效规则:
-
vector:插入可能导致所有迭代器失效;删除导致删除点及之后的迭代器失效
-
list:插入 / 删除只影响被删除元素的迭代器,其他不受影响
-
map/set:插入不失效;删除只影响被删除元素的迭代器
-
unordered_*:插入可能导致重哈希,所有迭代器失效
-
越界访问
std::vector<int> vec = {1, 2, 3};auto it = vec.begin();// 错误:移动到了end()之后while (it <= vec.end()) { // 应该用 != 而不是 <=std::cout << *it++;}
注意end () 返回的是 “最后一个元素的下一个位置”,不能解引用(*it)。判断迭代器是否到达末尾应该用!=,而不是<=,因为不是所有迭代器都支持<比较。
-
const 迭代器的使用
const迭代器表示只读迭代器,也就是不能通过它来对元素进行修改。对于只需要读访问元素的场景,使用const迭代器可以避免意外的修改,例如:
std::vector<int> vec = {1, 2, 3};auto it = vec.cbegin(); // const迭代器// *it = 10; // 错误!const迭代器不能修改元素// 正确:需要修改元素用非const迭代器auto mut_it = vec.begin();*mut_it = 10; // 没问题
在C++11中,cbegin()和cend()返回的就是 const 迭代器,即使容器本身不是 const 的,也能保证不修改元素。
-
迭代器的生命周期
迭代器的生命周期不能超过它所指向的容器,容器销毁后,迭代器也随之失效。
std::vector<int> get_vector() {return {1, 2, 3};}// 危险!auto it = get_vector().begin();// *it; // 临时vector已销毁,迭代器失效
总结:迭代器的使用注意事项
-
优先使用 auto 声明迭代器
减少代码冗余,避免类型错误
// 推荐auto it = vec.begin();// 不推荐std::vector<int>::iterator it = vec.begin();
-
优先使用迭代器而非下标
除非确定是随机访问容器,迭代器的通用性更好
// 通用写法,适用于所有容器for (auto it = container.begin(); it != container.end(); ++it)// 只适用于随机访问容器,通过下标访问for (size_t i = 0; i < container.size(); ++i)
-
处理插入删除时,避免迭代器失效
// 正确的删除方式for (auto it = vec.begin(); it != vec.end(); ) {if (*it % 2 == 0) {it = vec.erase(it); // 用返回值更新迭代器} else {++it;}}
-
需要只读访问时,使用 const 迭代器
提高代码可读性和安全性
for (auto it = vec.cbegin(); it != vec.cend(); ++it) {*it = 2; // 编译报错,不可修改}
-
不要把迭代器当指针用
避免用指针的思维操作迭代器(比如&(*it)取地址后长期持有),防止容器修改后内存失效
迭代器看似简单,但真正用好需要不断实践。这里也只是列举了迭代器的一些基本用法,还有一些进阶用法比如自定义迭代器等,等以后深入学习了再来总结
更多推荐



所有评论(0)