C++: STL 迭代器

第34-35课我们学了STL容器和算法。

现在,我们要深入STL的" glue"——迭代器

理解了迭代器,才能真正理解STL的设计哲学。


1. 迭代器概述

(1) 1.1 什么是迭代器?

迭代器是STL的核心概念,它连接容器和算法

生活类比:


(2) 1.2 迭代器的基本操作

所有迭代器都支持以下操作:

操作 说明 示例
*it 解引用 int x = *it;
it++ 前进到下一个元素 ++it;
it-- 后退到前一个元素 --it;
it1 == it2 比较相等 if (it1 == it2)
it1 != it2 比较不等 while (it != end())


2. 迭代器分类

(1) 2.1 五种迭代器

STL定义了5种迭代器,功能从弱到强:

迭代器类型 功能 代表容器
输入迭代器 只读,单向 istream_iterator
输出迭代器 只写,单向 ostream_iterator
前向迭代器 读写,单向 forward_list
双向迭代器 读写,双向 listsetmap
随机访问迭代器 读写,随机访问 vectordequearray

(2) 2.2 迭代器能力对比

TEXT 📖 仅展示
输入迭代器 ← 最弱
 ↓
前向迭代器
 ↓
双向迭代器
 ↓
随机访问迭代器 ← 最强

能力越强,支持的操作越多:

操作 输入 前向 双向 随机访问
解引用 *
前进 ++
后退 --
随机访问 ``
算术运算 + -

(3) 2.3 示例:不同容器的迭代器

CPP
#include <iostream>
#include <vector>
#include <list>
#include <forward_list>

int main() {
 std::vector<int> v = {1, 2, 3};
 std::list<int> l = {1, 2, 3};
 std::forward_list<int> fl = {1, 2, 3};
 
 // vector:随机访问迭代器
 auto it_v = v.begin();
 std::cout << it_v[2] << std::endl; // 可以随机访问
 
 // list:双向迭代器
 auto it_l = l.begin();
 ++it_l; // 可以前进
 --it_l; // 可以后退
 // it_l[2]; // ❌ 错误!list不支持随机访问
 
 return 0;
}

输出:

TEXT 📖 仅展示
3


3. 迭代器失效

(1) 3.1 什么是迭代器失效?

迭代器失效是指:容器操作后,迭代器指向的位置变得无效

常见原因:


(2) 3.2 不同容器的迭代器失效规则

容器 操作 失效情况
vector push_back 可能失效(重新分配时)
vector erase 被删元素及之后的迭代器失效
list push_back 不失效
list erase 只有被删元素的迭代器失效
map/set erase 只有被删元素的迭代器失效

(3) 3.3 示例:vector迭代器失效(难度⭐⭐)

▶ 示例 1:STL容器使用(难度⭐)

CPP
#include <iostream>
#include <vector>

int main() {
 std::vector<int> v = {1, 2, 3, 4, 5};
 
 auto it = v.begin();
 std::cout << "*it = " << *it << std::endl; // 输出:1
 
 // push_back可能导致重新分配,迭代器失效
 v.push_back(6);
 
 // ❌ 危险!it可能失效
 // std::cout << "*it = " << *it << std::endl; // 未定义行为
 
 // ✅ 正确做法:重新获取迭代器
 it = v.begin();
 std::cout << "*it = " << *it << std::endl; // 输出:1
 
 return 0;
}
▶ 试一试

输出:

TEXT 📖 仅展示
*it = 1
*it = 1

(4) 3.4 安全删除元素

错误做法:

CPP
for (auto it = v.begin(); it != v.end(); ++it) {
### ▶ 示例 2:现代C++特性应用(难度⭐)

 if (*it % 2 == 0) {
 v.erase(it); // ❌ it失效!
 }
}

正确做法:

CPP
for (auto it = v.begin(); it != v.end(); ) {
 if (*it % 2 == 0) {
 it = v.erase(it); // ✅ erase返回下一个有效迭代器
 } else {
 ++it;
 }
}

输出:

TEXT 📖 仅展示
(程序输出)


4. 反向迭代器

(1) 4.1 什么是反向迭代器?

反向迭代器从容器末尾向前遍历。

示例:逆序输出(难度⭐)

CPP
#include <iostream>
#include <vector>

int main() {
 std::vector<int> v = {1, 2, 3, 4, 5};
 
 // 使用反向迭代器
 for (auto it = v.rbegin(); it != v.rend(); ++it) {
 std::cout << *it << " ";
 }
 std::cout << std::endl;
 
 return 0;
}

运行结果:

TEXT 📖 仅展示
5 4 3 2 1 


5. 插入迭代器

(1) 5.1 什么是插入迭代器?

插入迭代器是一种输出迭代器,用于向容器插入元素

三种插入迭代器:

迭代器 功能 示例
back_inserter 尾部插入 std::back_inserter(v)
front_inserter 头部插入 std::front_inserter(l)
inserter 指定位置插入 std::inserter(v, v.begin())

(2) 5.2 示例:用back_inserter复制(难度⭐⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

int main() {
 std::vector<int> src = {1, 2, 3, 4, 5};
 std::vector<int> dst; // 空容器
 
 // ❌ 错误!dst空间不足
 // std::copy(src.begin(), src.end(), dst.begin());
 
 // ✅ 正确!用back_inserter自动扩容
 std::copy(src.begin(), src.end(), std::back_inserter(dst));
 
 std::cout << "复制结果:";
 for (int x : dst) {
 std::cout << x << " ";
 }
 std::cout << std::endl;
 
 return 0;
}

输出:

TEXT 📖 仅展示
1 2 3 4 5


6. 流迭代器

(1) 6.1 输入流迭代器

功能: 从输入流读取数据。

示例:从标准输入读取(难度⭐⭐)

CPP
#include <iostream>
#include <vector>
#include <iterator>

int main() {
 std::vector<int> v;
 
 std::cout << "输入几个数字(Ctrl+Z结束):" << std::endl;
 
 // 从标准输入读取
 std::copy(std::istream_iteratorint(std::cin),
 std::istream_iteratorint(),
 std::back_inserter(v));
 
 std::cout << "你输入了:";
 for (int x : v) {
 std::cout << x << " ";
 }
 std::cout << std::endl;
 
 return 0;
}

(2) 6.2 输出流迭代器

功能: 向输出流写入数据。

示例:输出到文件(难度⭐⭐)

CPP
#include <iostream>
#include <vector>
#include <iterator>
#include <fstream>

int main() {
 std::vector<int> v = {1, 2, 3, 4, 5};
 
 // 输出到标准输出
 std::copy(v.begin(), v.end(),
 std::ostream_iteratorint(std::cout, " "));
 std::cout << std::endl;
 
 // 输出到文件
 std::ofstream file("output.txt");
 std::copy(v.begin(), v.end(),
 std::ostream_iteratorint(file, "\n"));
 file.close();
 
 return 0;
}

❓ 常见问题

Q 为什么list不支持随机访问?
A list 是链表,元素在内存中不连续,无法用索引直接访问。

Q 迭代器失效怎么避免?
A 1. 每次容器操作后,重新获取迭代器 2. 用 erase 的返回值更新迭代器 3. 尽量用算法,少手动操作迭代器

Q:const_iterator是什么? A:const_iterator 是只读迭代器,不能修改元素值。

CPP
std::vector<int> v = {1, 2, 3};
std::vector<int>::const_iterator it = v.cbegin();
// *it = 10; // ❌ 错误!不能修改

▶ 示例 3:迭代器遍历(难度⭐)

CPP
#include <iostream>
#include <vector>

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5};

    for (auto it = v.begin(); it != v.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;

    return 0;
}
▶ 试一试

输出:

TEXT 📖 仅展示
1 2 3 4 5
💡 提示:迭代器像指针一样,用 * 解引用,用 ++ 移动,用 != 比较位置。


知识点 要点
迭代器分类 输入→前向→双向→随机访问
迭代器失效 vector可能失效,list不会
反向迭代器 rbegin()/rend()
插入迭代器 back_inserter
流迭代器 连接STL和IO

📖 小节

📝 作业

  1. **基础题 (Difficulty ⭐):用迭代器遍历 vector<int> 并输出所有元素。分别用 begin/end 和范围 for 两种方式。

  2. **进阶题 (Difficulty ⭐⭐):用反向迭代器 rbegin/rend 倒序遍历 vector 并输出。观察与正序遍历的区别。

  3. **挑战题 (Difficulty ⭐⭐⭐):实现一个自定义迭代器,包装一个整数范围(如 1 到 10),支持 ++ 和 * 操作符。



下一课:STL函数对象(#38)

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏