当前位置: 首页 > web >正文

C++ STL中迭代器学习笔记

文章目录

  • 前言
  • 一、迭代器到底是什么?
  • 二、五种迭代器类型
  • 三、Traits 技术:类型萃取的魔法
  • 四、标签分发:用“空类”实现重载策略
  • 五、常用适配器介绍(配合容器更强大)
  • 六、源码中 __normal_iterator 的作用
  • 七、侯捷源码剖析特色总结
  • 八、结语:学习迭代器的意义


前言

初学C++ 时一提 STL 头就大,尤其看到源码中复杂的 __iterator_traitsadvance__distance 等等时,直接劝退。而在侯捷老师的《STL源码剖析》中,迭代器的讲解尤为精彩,深入浅出地剖析了“迭代器本质就是一个聪明指针”。

一、迭代器到底是什么?

通俗讲:迭代器是一个用来遍历容器的“泛型指针”
在 C 语言里我们操作数组靠指针,而 C++ STL 容器结构多样:vector 是动态数组,list 是双向链表,map 是红黑树。那么算法如 sort、copy 如何统一处理?——这就要靠迭代器。
迭代器本质上是一个封装了指针行为的对象,重载了:

  • *p 访问元素
  • ++p / --p 移动
  • p == q 比较位置

侯捷老师总结:指针是最原始的迭代器,而 STL 中的迭代器就是“行为像指针的类”。

二、五种迭代器类型

STL 根据操作能力,把迭代器分为五大类,每种都是前一种的超集。

类型能力举例容器
InputIterator(输入)只读,前移istream_iterator
OutputIterator(输出)只写,前移ostream_iterator
ForwardIterator(前向)可读写,前移forward_list
BidirectionalIterator(双向)可读写,前后移动list、set
RandomAccessIterator(随机访问)可读写,可跳跃vector、deque

你可以理解为五个“段位”:
Input < Forward < Bidirectional < RandomAccess

侯捷提到:STL 的设计哲学是“最小需求设计”,算法根据最弱要求选择最小类型,然后在运行时通过模板 + traits 实现“能力分发”。

三、Traits 技术:类型萃取的魔法

当我们写一个泛型算法时,并不知道传入的迭代器是啥类型。这时就靠iterator_traits这个结构体提取类型信息:

template<typename Iterator>
struct iterator_traits {using iterator_category = typename Iterator::iterator_category;using value_type = typename Iterator::value_type;using difference_type = typename Iterator::difference_type;...
};

这样就能在算法中写:

typename iterator_traits<Iter>::iterator_category()

来获得标签(如 random_access_iterator_tag),再通过函数重载进行调度。

👉 侯捷称其为 “类型的标签分发器”。

注意:
在定义类模板或者函数模板时,typename 和 class 关键字都可以用于指定模板参数中的类型。也就是说,以下两种用法是完全等价的。

template<typename T> /* ... */;
template<class T>    /* ... */;

核心问题:泛型算法中的迭代器不确定性
当我们编写泛型算法时(如 std::advancestd::distance),算法需要处理任意类型的迭代器:

template <typename Iter>
void my_algorithm(Iter first, Iter last) {// 这里不知道 Iter 是什么类型的迭代器// 应该用 ++it 还是 it + n?
}

iterator_traits 的解决方案
iterator_traits 是一个类型萃取(type trait)工具,它提供了标准化的方式来获取迭代器的属性:

template<typename Iterator>
struct iterator_traits {// 从迭代器类型本身提取信息using iterator_category = typename Iterator::iterator_category;using value_type = typename Iterator::value_type;using difference_type = typename Iterator::difference_type;// ...
};

关键设计:迭代器标签(Tags)
迭代器标签是空结构体,用于标识迭代器的能力等级:

struct input_iterator_tag {};
struct output_iterator_tag {};
struct forward_iterator_tag : input_iterator_tag {};
// ... 其他标签

标签分发(Tag Dispatching)机制
在算法中,我们这样使用标签:

template <typename Iter>
void my_advance(Iter& it, int n) {// 获取迭代器标签类型using category = typename iterator_traits<Iter>::iterator_category;// 创建标签实例并分发到具体实现my_advance_impl(it, n, category{});
}

具体实现的分派
针对不同标签提供不同的实现:

// 基础实现(输入迭代器)
template <typename Iter>
void my_advance_impl(Iter& it, int n, input_iterator_tag) {while (n-- > 0) ++it;
}// 优化实现(随机访问迭代器)
template <typename Iter>
void my_advance_impl(Iter& it, int n, random_access_iterator_tag) {it += n;  // 直接跳转,O(1) 复杂度
}

四、标签分发:用“空类”实现重载策略

advancedistance 函数中,STL 利用标签分发实现了“为不同迭代器走不同逻辑”的技巧:

例:advance() 实现

template <typename InputIterator, typename Distance>
void advance(InputIterator& it, Distance n) {_advance(it, n, iterator_traits<InputIterator>::iterator_category());
}void _advance(InputIterator& it, Distance n, input_iterator_tag) {while (n--) ++it;
}void _advance(BidirectionalIterator& it, Distance n, bidirectional_iterator_tag) {if (n >= 0) while (n--) ++it;else while (n++) --it;
}void _advance(RandomAccessIterator& it, Distance n, random_access_iterator_tag) {it += n;
}

这样就能根据it的类型自动调度最高效版本,而不需要用户干预。你不写 if,编译器帮你选最优路径。
这就是模板 + 类型标签的魅力,侯捷称其为 STL 最经典技巧之一。

五、常用适配器介绍(配合容器更强大)

STL 提供了很多“迭代器适配器”,用于将普通容器包装为“具有特殊行为”的对象。
1)reverse_iterator:反向迭代器
让你从容器尾部向前遍历。

vector<int> v = {1,2,3,4};
for (auto rit = v.rbegin(); rit != v.rend(); ++rit)cout << *rit; // 输出 4 3 2 1

它的 base() 实际指向正向迭代器的“后一位”,所以 *rit 实际是 *(base() - 1)

2)back_insert_iterator:尾部插入器
把赋值操作转成容器的 push_back

vector<int> v;
auto it = back_inserter(v);
*it = 1;  // 等价于 v.push_back(1);

适合配合 copy 使用:

copy(src.begin(), src.end(), back_inserter(dest));

3)insert_iterator:中间插入器
让插入发生在指定位置上:

insert_iterator it(v, v.begin() + 3);
*it = 99;  // 插入在第4位前

六、源码中 __normal_iterator 的作用

侯捷特别分析了 __normal_iterator:STL 的 vector、string 的迭代器其实就是对原生指针加壳。

template<typename T>
class __normal_iterator {T* ptr;
public:__normal_iterator& operator++() { ++ptr; return *this; }reference operator*() const { return *ptr; }...
};

为什么不直接用裸指针?为了统一接口(traits支持、自定义功能等),让 vector<int>::iterator 看起来也是一个类,而不仅仅是 int*

七、侯捷源码剖析特色总结

侯捷老师在书中对迭代器实现做了大量细节还原,比如:

  • 自己写 __type_traits 结构体讲解
  • distance_type()value_type() 这些函数如何萃取类型
  • 反复强调 template + traits + tag dispatching 是 STL 三大法宝

这些思想你在看完这篇文章后再读《STL 源码剖析》会更有感觉。

八、结语:学习迭代器的意义

理解 STL 迭代器,不只是为了写for (auto it = ...)而已,更重要的是:

  1. 看懂源码 / 编写泛型库
  2. 掌握 STL 高性能设计技巧
  3. 为自己写容器打基础
  4. 为面试算法题打通容器适配技巧

就像侯捷说的:“STL 是 C++ 的皇冠,迭代器是皇冠上的明珠。” 把这颗明珠理解透,你就打开了 C++ 泛型编程的大门。

http://www.xdnf.cn/news/15984.html

相关文章:

  • Docker实践:使用Docker部署WhoDB开源轻量级数据库管理工具
  • AI大模型学习路线-全技术栈
  • HTML和CSS快速入门
  • Spring之AOP面向切面编程详解
  • 试用SAP BTP 06:AI服务-Data Attribute Recommendation
  • 数据结构-线性表顺序表示
  • 基于单片机无线防丢/儿童防丢报警器
  • RabbitMQ:解锁高效消息传递的密码[特殊字符]
  • playwright 最佳实践
  • 【web安全】SQL注入与认证绕过
  • MPLS-LDP
  • 小红书 MCP 服务器
  • ADC和DMA简述
  • 渗透笔记(XSS跨站脚本攻击)
  • Linux之dpkg--命令的用法
  • 软件测试-Bug
  • 41.FeignClient整合Sentinel
  • 【C++】C++入门
  • 氛围编码(Vice Coding)的工具选择方式
  • [CVPR]DVFL-Net:用于时空动作识别的轻量级蒸馏视频调焦网络
  • 华为开源自研AI框架昇思MindSpore应用案例:基于ERNIE模型实现对话情绪识别
  • Spring 事务和事务传播机制
  • CSS 单位完全指南:掌握 em、rem、vh、vw 等响应式布局核心单位
  • 仙盟数据库应用-外贸标签打印系统 前端数据库-V8--毕业论文-—-—仙盟创梦IDE
  • 单链表专题
  • docker compose 编排容器 mysql Springboot应用
  • 使用pnpm安装项目的生产依赖dependencies和开发依赖devDependies及pnpm工作空间等简单使用方法说明
  • 全面解析MySQL(2)——CRUD基础
  • SQL 调优第一步:EXPLAIN 关键字全解析
  • HTTP1-HTTP2-HTTP3简要概述