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

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.dtcms.com/a/289838.html

相关文章:

  • day057-docker-compose案例与docker镜像仓库
  • 元学习算法的数学本质:从MAML到Reptile的理论统一与深度分析
  • Vision Transformer (ViT) 介绍
  • 面试高频题 力扣 417. 太平洋大西洋水流问题 洪水灌溉(FloodFill) 深度优先遍历(dfs) 暴力搜索 C++解题思路 每日一题
  • 使用unsloth模型微调过程
  • 软件反调试(5)- 基于注册表实时调试器检测
  • MYSQL:从增删改查到高级查询
  • 数据结构-线性表的链式表示
  • 《P3398 仓鼠找 sugar》
  • 【1】YOLOv13 AI大模型-可视化图形用户(GUI)界面系统开发
  • 【实证分析】会计稳健性指标分析-ACF、CScore、Basu模型(2000-2023年)
  • MySQL锁(二) 共享锁与互斥锁
  • Filter快速入门 Java web
  • Compose笔记(三十七)--FilterChip
  • TVLT:无文本视觉-语言Transformer
  • c++ duiLib 显示一个简单的窗口
  • AMD处理器 5700G 矿卡RX580-8G 打英雄联盟怎么样
  • 洛谷 P10287 [GESP样题 七级] 最长不下降子序列-普及/提高-
  • 《P2680 [NOIP 2015 提高组] 运输计划》
  • 【66】MFC入门到精通——(CComboBox)下拉框选项顺序与添加顺序不一致
  • 前端静态资源免费cdn服务推荐
  • Dify极简部署手册
  • 30天打好数模基础-逻辑回归讲解
  • 7-大语言模型—指令理解:指令微调训练+模型微调
  • 【算法训练营Day15】二叉树part5
  • 编程研发工作日记
  • 050_Set接口(HashSet / TreeSet / LinkedHashSet)
  • 力扣面试150题--搜索插入位置
  • 某市公安局视频图像信息综合应用平台设计方案Word(446页)
  • AI产品经理面试宝典第40天:用户反馈处理与技术应用面试题与答法