C++笔记 STL lterator迭代器

在C++ STL(标准模板库)中,迭代器(iterator)是连接容器与算法的"桥梁"。它本质是一种"泛化的指针",封装了底层容器的访问逻辑,让我们无需关注容器的内部实现,就能统一、便捷地遍历容器中的元素------无论是vector、list、map,还是set,都能通过迭代器实现一致的遍历操作,这也是STL"算法与容器解耦"的核心设计思想。

本文从迭代器的基础认知、分类、核心用法、底层原理、常见误区五个维度,整理迭代器的核心知识点,适配新手学习与复习,兼顾实用性和应试性(面试常考)。

一、迭代器基础认知

1. 什么是迭代器?

迭代器是STL中定义的模板类/结构体,本质是"行为类似指针"的对象,提供了对容器元素的间接访问能力。它屏蔽了不同容器(如vector的连续内存、list的链表内存)的底层差异,让我们可以用统一的语法(如*it取值、++it移动)访问任何容器的元素。

简单来说:迭代器 = 通用版指针,指针是迭代器的一种特殊形式(针对数组),而迭代器可以适配所有STL容器。

2. 迭代器的核心作用

遍历容器:依次访问容器中的每一个元素,是STL遍历容器的标准方式。

元素操作:通过迭代器取值(*it)、修改元素(非const迭代器)。

连接算法与容器:STL算法(如sort、find)无需关心容器类型,只需接收迭代器范围,就能对容器进行操作(如sort只需传入容器的begin和end迭代器)。

3. 迭代器的头文件

迭代器无需单独包含头文件,只要包含对应容器的头文件,就会自动引入该容器的迭代器定义:

cpp 复制代码
#include <vector>  // 包含vector容器,同时引入vector::iterator
#include <list>   // 包含list容器,同时引入list::iterator
#include &lt;map&gt;    // 包含map容器,同时引入map::iterator

若需使用通用迭代器相关工具(如迭代器适配器),需包含头文件 #include <iterator>

二、迭代器的分类(按功能划分,面试重点)

STL迭代器按功能和访问能力,分为5类,核心常用的是前4类,从弱到强依次为:输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机访问迭代器。不同容器支持的迭代器类型不同,这也决定了容器能支持的算法(如sort需要随机访问迭代器)。

1. 输入迭代器(Input Iterator)

最基础的迭代器,仅支持"只读"和"单向移动(++)",不能修改元素,也不能反向移动(--)。

支持操作:++it(前缀/后缀)、*it(取值,只读)、==!=

适用容器/场景:istream_iterator(输入流迭代器),仅用于读取数据,不用于容器遍历。

2. 输出迭代器(Output Iterator)

仅支持"只写"和"单向移动(++)",不能读取元素,也不能反向移动。

支持操作:++it*it = val(赋值,只写)。

适用容器/场景:ostream_iterator(输出流迭代器),仅用于写入数据,不用于容器遍历。

3. 前向迭代器(Forward Iterator)

继承输入/输出迭代器的功能,支持"读写"和"单向移动(++)",可以重复遍历同一范围(多次++)。

支持操作:输入/输出迭代器的所有操作,可重复访问同一元素。

适用容器:forward_list(单向链表)。

4. 双向迭代器(Bidirectional Iterator)

继承前向迭代器的功能,新增"反向移动(--)",支持双向遍历。

支持操作:前向迭代器的所有操作 + --it(前缀/后缀)。

适用容器:list(双向链表)、set、multiset、map、multimap。

5. 随机访问迭代器(Random Access Iterator)

功能最强的迭代器,继承双向迭代器的功能,支持"随机访问"(直接访问任意位置元素),速度最快。

支持操作:双向迭代器的所有操作 +it + nit - nit += nit -= nit1 - it2(计算两个迭代器的距离)、[](如it[0]等价于*(it+0))。

适用容器:vector、deque、array(数组),也是最常用的迭代器类型。

核心总结(表格对比,面试直接背)

迭代器类型 核心能力 支持的容器
输入迭代器 只读、单向移动 istream_iterator
输出迭代器 只写、单向移动 ostream_iterator
前向迭代器 读写、单向移动、可重复遍历 forward_list
双向迭代器 读写、双向移动 list、set、map 等
随机访问迭代器 读写、双向移动、随机访问 vector、deque、array

三、迭代器的核心用法(最常用,必掌握)

迭代器的使用流程:获取迭代器 → 移动迭代器 → 操作元素,不同容器的迭代器用法基本一致,以下以最常用的vector(随机访问迭代器)和list(双向迭代器)为例,讲解通用用法。

1. 迭代器的基本定义与初始化

每个容器都有对应的迭代器类型,格式为:容器类型::iterator(非const迭代器,可读写)、容器类型::const_iterator(const迭代器,只读)。

cpp 复制代码
#include <vector>
#include <iostream>
using namespace std;

int main() {
    vector<int> v = {1, 2, 3, 4, 5};
    
    // 1. 非const迭代器(可读写)
    vector<int>::iterator it;
    it = v.begin();  // 指向容器第一个元素
    
    // 2. const迭代器(只读,不能修改元素)
    vector<int>::const_iterator cit = v.cbegin();  // cbegin() 返回const迭代器
    
    // 3. 反向迭代器(从容器末尾开始遍历)
    vector<int>::reverse_iterator rit = v.rbegin();  // rbegin() 指向最后一个元素
    
    return 0;
}

2. 迭代器的核心操作(以vector为例)

cpp 复制代码
vector<int> v = {1, 2, 3, 4, 5};
vector<int>::iterator it = v.begin();  // 指向第一个元素(1)

// 1. 取值:*it
cout << *it << endl;  // 输出:1

// 2. 移动迭代器:++it(前缀,效率更高)、it++(后缀)
++it;  // 指向第二个元素(2)
cout << *it << endl;  // 输出:2

// 3. 随机访问(只有随机访问迭代器支持)
it += 2;  // 从2移动到4(跳过3)
cout << *it << endl;  // 输出:4
cout << it[1] << endl;  // 等价于*(it+1),输出:5

// 4. 遍历容器(最常用写法)
for (vector<int>::iterator it = v.begin(); it != v.end(); ++it) {
    cout << *it << " ";  // 输出:1 2 3 4 5
}

// 5. 修改元素(非const迭代器)
*it = 10;  // 将当前指向的元素(5)改为10

3. 双向迭代器用法(以list为例)

list的迭代器是双向迭代器,不支持随机访问(不能用it + nit[]),只能用++it--it移动:

cpp 复制代码
#include <list>
using namespace std;

int main() {
    list<int> l = {10, 20, 30};
    list<int>::iterator it = l.begin();
    
    ++it;  // 指向20(支持)
    --it;  // 指向10(支持)
    // it += 2;  // 报错!双向迭代器不支持随机访问
    
    // 遍历(只能用++/--)
    for (auto it = l.begin(); it != l.end(); ++it) {
        cout << *it << " ";  // 输出:10 20 30
    }
    return 0;
}

4. C++11 简化写法(auto自动推导迭代器类型)

C++11及以上支持auto关键字,无需手动写迭代器类型,简化代码(最常用,推荐):

cpp 复制代码
vector<int> v = {1, 2, 3};
// auto 自动推导为 vector<int>::iterator
for (auto it = v.begin(); it != v.end(); ++it) {
    cout << *it << " ";
}

// const迭代器简化(只读)
for (auto it = v.cbegin(); it != v.cend(); ++it) {
    // *it = 10;  // 报错,只读
}

// 反向迭代器简化
for (auto it = v.rbegin(); it != v.rend(); ++it) {
    cout << *it << " ";  // 输出:3 2 1
}

四、迭代器的底层原理(面试考点)

迭代器本质是"封装了指针的模板类",不同容器的迭代器底层实现不同,核心是适配容器的内存结构,实现统一的访问接口。

1. 随机访问迭代器(vector)底层实现

vector的底层是连续内存(动态数组),所以其迭代器本质就是T*(普通指针),所有随机访问操作(it + nit[])都是直接对指针进行操作,效率极高。

简化底层逻辑(伪代码):

cpp 复制代码
template <typename T>
class vector_iterator {
private:
    T* ptr;  // 底层就是普通指针
public:
    // 构造函数
    vector_iterator(T* p) : ptr(p) {}
    // 取值
    T& operator*() { return *ptr; }
    // 移动迭代器
    vector_iterator& operator++() { ptr++; return *this; }
    // 随机访问
    vector_iterator& operator+(int n) { ptr += n; return *this; }
    // 其他操作(==、!=、[]等)...
};

2. 双向迭代器(list)底层实现

list的底层是双向链表,内存不连续,无法用普通指针直接访问下一个元素,因此其迭代器需要封装"链表节点指针",并实现++--操作(本质是移动到链表的下一个/上一个节点)。

简化底层逻辑(伪代码):

cpp 复制代码
// list的节点结构
template <typename T>
struct list_node {
    T data;
    list_node* prev;  // 前驱节点
    list_node* next;  // 后继节点
};

// list迭代器
template <typename T>
class list_iterator {
private:
    list_node<T>* node;  // 底层是链表节点指针
public:
    list_iterator(list_node<T>* n) : node(n) {}
    // 取值
    T& operator*() { return node->data; }
    // 移动到下一个节点
    list_iterator& operator++() {
        node = node->next;
        return *this;
    }
    // 移动到上一个节点
    list_iterator& operator--() {
        node = node->prev;
        return *this;
    }
    // 其他操作...
};

3. 核心总结

迭代器的底层实现依赖容器的内存结构:

连续内存容器(vector、deque):迭代器 = 封装的普通指针,支持随机访问,效率高。

非连续内存容器(list、set、map):迭代器 = 封装的节点指针,仅支持双向/前向移动,效率略低,但适配链表/树形结构。

所有迭代器都统一了"访问接口",因此算法可以无缝适配所有容器。

五、迭代器的常见误区与注意事项(避坑重点)

1. 迭代器失效(最常见、最容易踩坑)

迭代器失效:迭代器指向的内存位置失效(如内存被释放、元素被删除),此时使用迭代器(如*it++it)会导致未定义行为(程序崩溃、乱码)。

常见导致迭代器失效的场景:

场景1:vector扩容(push_back时触发扩容)------ 扩容会申请新内存、拷贝旧元素、释放旧内存,原迭代器指向旧内存,全部失效。

场景2:删除容器元素(erase)------ 删除元素后,该元素及其后面的迭代器失效(vector),或仅删除的迭代器失效(list)。

避坑建议:

vector扩容后,重新获取迭代器(如it = v.begin())。

删除元素时,用erase的返回值更新迭代器(仅vector、deque需要,list无需)。

避免在遍历过程中随意修改容器(如push_back、erase)。

2. const迭代器与非const迭代器的区别

iterator:非const迭代器,可读写元素,可修改容器内容。

const_iterator:const迭代器,只读元素,不能修改容器内容(常用于遍历const容器)。

注意:cbegin()cend() 返回const迭代器,begin()end() 返回非const迭代器。

3. 迭代器的赋值与比较

只有同一容器的迭代器才能赋值和比较(如vector的迭代器不能和list的迭代器比较)。

迭代器的==!= 表示"是否指向同一个元素",<> 仅随机访问迭代器支持(表示位置先后)。

4. 空容器的迭代器

空容器的begin()end() 相等(都指向"末尾之后"的位置),此时不能取值(*it),否则报错。

六、迭代器的应用场景(实战)

1. 遍历容器(最基础)

cpp 复制代码
// 遍历vector
vector<string> vs = {"a", "b", "c"};
for (auto it = vs.begin(); it != vs.end(); ++it) {
    cout << *it << endl;
}

// 遍历list
list<int> l = {1, 2, 3};
for (auto it = l.cbegin(); it != l.cend(); ++it) {
    cout << *it << " ";
}

2. 配合STL算法使用(核心场景)

STL算法(如sort、find、reverse)的参数都是迭代器范围,无需关心容器类型:

cpp 复制代码
#include <algorithm>  // 包含STL算法头文件

vector<int> v = {3, 1, 4, 1, 5};

// 1. 排序(sort需要随机访问迭代器,vector支持,list不支持)
sort(v.begin(), v.end());  // 升序排序,结果:1 1 3 4 5

// 2. 查找(find支持所有迭代器)
auto it = find(v.begin(), v.end(), 4);
if (it != v.end()) {
    cout << "找到元素:" << *it << endl;
}

// 3. 反转(reverse支持双向及以上迭代器)
reverse(v.begin(), v.end());  // 结果:5 4 3 1 1

七、总结(核心考点浓缩)

  1. 迭代器是STL容器与算法的桥梁,本质是泛化的指针,统一容器访问接口。

  2. 5类迭代器:输入、输出、前向、双向、随机访问,核心常用后4类,不同容器支持的迭代器类型不同。

  3. 核心用法:定义迭代器 → 用begin()/end()获取范围 → 移动迭代器 → 取值/修改(非const)。

  4. 底层:连续内存容器(vector)迭代器是封装指针,非连续内存(list)是封装节点指针。

  5. 重点避坑:迭代器失效(扩容、删除元素),删除元素用erase返回值更新迭代器。

  6. 面试高频:迭代器分类、迭代器失效场景、不同容器的迭代器类型。

相关推荐
paeamecium2 小时前
【PAT甲级真题】- Table Tennis (30)
c++·pat考试·pat
学习使我健康2 小时前
Android 广播介绍详情
android·开发语言·kotlin
lsx2024062 小时前
JavaScript Array(数组)
开发语言
小柯博客2 小时前
Amazon Kinesis Video Streams C WebRTC SDK 开发实战
c语言·开发语言·网络·stm32·嵌入式硬件·webrtc·yocto
rannn_1112 小时前
3h速通Python:用Java的思维看懂Python
开发语言·python·ai·ai agent·大模型应用开发
6Hzlia2 小时前
【Hot 100 刷题计划】 LeetCode 21. 合并两个有序链表 | C++ 经典迭代与 Dummy 技巧
c++·leetcode·链表
上弦月-编程2 小时前
C语言位运算:从入门到精通
运维·c语言·开发语言·vscode·算法·leetcode·极限编程
minglie12 小时前
c语言面向对象的led
c语言·开发语言
汉克老师2 小时前
GESP2023年6月认证C++三级( 第三部分编程题(2、密码合规检测))
c++·字符串·gesp三级·gesp3级