当前位置:网站首页>《STL 源码剖析》学习笔记之容器(二)list
《STL 源码剖析》学习笔记之容器(二)list
2022-07-29 18:08:00 【51CTO】

[图]The orange 2019-08-06
这是 herongwei 的第 83 篇原创
阅读本文大概需要 6.66 分钟
1、list 概述
相较于 vector 的连续线性空间,list 就显得复杂许多,它的好处是每次安插或删除一个元素,就配置或释放一个元素空间。因此,list 对于空间的运用有绝对的精准,一点也不浪费。而且,对于任何位置的元素安插或元素移除,list 永远是常数时间。
list 和 vector 是两个最常被使用的容器。什么时机下最适合使用哪一种容器,必须视元素的多寡、元素的构造复杂度、元素存取行为的特性而定。
list 结构定义

SGI list 不仅是一个双向串行,而且还是一个环状双向串行。所以它只需要一个指标,便可以完整表现整个串行:

安插一个元素之后的 list 状态如图 4-6。注意,安插完成后,新节点将位于标兵迭代器(标示出安插点)所指之节点的前方—这是 STL 对于「安插动作」的标准规范。
由于 list 不像 vector那样有可能在空间不足时做重新配置、数据搬移的动作,所以安插前的所有迭代器在安插动作之后都仍然有效。

2、几点注解
list 内部提供一个所谓的迁移动作(transfer):将某连续范围的元素迁移到某个特定位置之前。技术上很简单,节点间的指标移动而已。这个动作为其它的复杂动作如 splice, sort, merge 等奠定良好的基础。
1、node 是一个指针,SGI list 是一种双向环状链表,除了绿色的数据域本身之外,还要附带两个指针,一个往前指,一个往后指,也就是前驱指针和后继指针。
2、前驱和后继指针分别是 void_pointer 类型,看源码知道,void_pointer 是 void* 类型。原因?可见在程序后面操作要去转化类型。
3、list 的迭代器如何使用?链表是一种非连续的结构,所以迭代器不能是一种指针,但是我们还是要让迭代器要模拟指针的功能,也就是说,迭代器要足够聪明的知道,当用户要访问下一个元素的时候,迭代器要能够通过当前指向的位置的指针的 next 域,定位到正确访问的下一个位置,进去看 next 指针。
4、除了 array,vector 之外,所有的容器的迭代器必须是一种 class ,是一种智能指针,它才能设计出聪明的动作。
5、所有的容器必须要有一个 typedef xxx<T,T&,T> iterator 代表一种 class。有三个模板参数。
6、指针可能会被使用者进行怎样的操作呢?++,–,&,等等。
7、重载运算符 ++
调整迭代器的指向,使得指向当前结点的next结点
8、重载运算符 --也是一样
9、这里的两个返回值,一个返回 reference 一个不是,是为了模拟整数的这两种操作,在 C++ 中,两次前置++可以运行,但不允许两次后置++。
10、node 结点的取值操作,为什么这里要用.data 而不是直接用 -> ,这里有个原因在于为了让编译器区分后面的重载-> 运算符
11、所有容器遵循的设计原则是 前闭后开原则,在 list 里面,要把最后一个元素的下一个元素设置为不属于容器本身。因此,list 的设计刻意在环状尾端加一个空白结点。
12、在 G2.9 版 中 sizeof(list) = 4而在 G4.9 版 sizeof(list) = 8;
3、源码剖析
还记得之前的手写 LRU 吗?
我们知道,STL 中的 list 就是一种双向链表,我们在定义一个 cachesMap ,key 保存键,value 保存前面的 list 中指向双向链表实现的 LRU 的 Node 节点,也就是说 cachesMap 里面 每一个 value 对应的是一个双向链表的结点,那么具体访问的时候,利用迭代器就可以了,非常的方便。
LRU 的 Node 节点,也就是说 cachesMap 里面 每一个 value 对应的是一个双向链表的结点,那么具体访问的时候,利用迭代器就可以了,非常的方便。
这里,爱思考的同学们,看到上面的 splice 函数,不禁会思考,这个函数到底是如何实现的呢?本真刨根问底的精神,我们去探究一波 list 的源码吧。
先来看一张图片

没错了,上面的图示就是 splice 函数的实现过程,而在 list 里面,具体调用的则是 另外一个函数 transfer,下面是 transfer 的源码:
可以看到
第一步是调整 last 结点前一个结点的 next 指针;这样才能在调整 tmp 结点的 next 指针之后,将 first position 结点保存下来。
第二步是调整 first 结点的前一个结点的 next 指针;这样才能在调整 first 结点的 prev 指针之后,将 last 结点保存下来。
实际上,transfer 并非是一种公开的接口。list 公开提供的是所谓的接合动作(splice):将某连续范围的元素从一个 list 搬移到另一个(或同一个)list 的某个定点。
在来看一张比较形象的图片就大概知道了这个过程是怎么一回事了。

下面附上部分 list 源码。
参考资料:《STL源码剖析》(侯捷著)。
感兴趣的同学,欢迎有问题和我交流~
推荐阅读
《STL 源码剖析》学习笔记之容器(一)vector

认真的人,自带光芒!
原创不易
点个呗

边栏推荐
- [Code Hoof Set Novice Village 600 Questions] Detailed explanation of pow() function
- 【码蹄集新手村600题】pow()函数详解
- 公司无线规划设计及实施SOP
- 恐造成下一个“千年虫”的闰秒,遭科技巨头们联合抵制
- 实时数仓:咸鱼的实时数仓经验分享
- 如何实时计算日累计逐单资金流
- Google Cloud X Kyligence|如何从业务视角管理数据湖?
- [Code Hoof Set Novice Village 600 Questions] Given an integer n, find all the values of x and y in floor(n/x)=y
- HCIP笔记第十四天
- Network Effects in Web3
猜你喜欢

How different DAOs are changing the world

LL(1),LR(0),SLR(1),LALR(1),LR(1)对比与分析

字节跳动 Flink 单点恢复功能及 Regional CheckPoint 优化实践

华东师范大学副校长周傲英:数据赋能,从数据库到数据中台

ARTS-第-25-期

【7.23-7.29】博客精彩回顾

MySQL 中的反斜杠 \\,真是太坑了

String类型_static成员_动态内存分配_拷贝构造函数_const关键字_友元函数与友元类

KubeMeet 报名 | 「边缘原生」线上技术沙龙完整议程公布!

开放原子开源基金会秘书长孙文龙:要打造以开发者为本的开源服务平台
随机推荐
FPGA设计16位二进制全加器模块
已经删除了的SQL节点,有没有办法恢复
维信诺与荣耀终端已签署8.91亿元订单
一文了解信创背景下 SAN 存储转型路线
实时数仓:知乎实时数仓的架构演进
swin-transformer初步理解
160亿美元!传微软计划收购全球最大语音识别公司
数据监控体系是什么?该怎么搭建?
P4775 [NOI2018] 情报中心(线段树合并)
[Code Hoof Set Novice Village 600 Questions] Detailed explanation of pow() function
Zadig 环境负载均衡:0 人工干预,极速部署
【深度学习】使用yolov5对数据进行预标注
[Code Hoof Set Novice Village 600 Questions] Given an integer n, find all the values of x and y in floor(n/x)=y
5年迭代5次,抖音推荐系统演进历程
Web APIs DOM- 网页特效篇 综合案例
xatlas源码解析(七)
联发科天玑2000最快Q3量产,4G基带芯片将拿下Apple Watch订单
罚款182.28亿元!市场监管总局针对阿里巴巴垄断行为做出行政处罚
亿级用户背后的字节跳动云原生计算最佳实践
多线程并发Callable