【C++笔记】list结构剖析及其模拟实现
【C++笔记】list结构剖析及其模拟实现

🔥个人主页:大白的编程日记
🔥专栏:C++笔记

文章目录
- 【C++笔记】list结构剖析及其模拟实现
- 前言
- 一 .list的结构及其介绍
- 1.1list的结构
- 1.2list的使用
- 1.3迭代器划分
- 二.list的模拟实现
- 2.1 list结构的定义
- 2.2iterator迭代器
- 2.3list迭代器
- 2.3构造
- 2.4拷贝构造
- 2.5 operator=
- 2.6 insert
- 2.7 erase
- 2.8 clear
- 2.9析构函数
- 三.按需实例化
- 四.迭代器失效
- 五.initializer_list
- 后言
前言
哈喽,各位小伙伴大家好!上期我们讲了vector和深浅拷贝。今天我们来讲一下list及其实现。话不多说,我们进入正题!向大厂冲锋
一 .list的结构及其介绍
1.1list的结构

list的底层结构是一个带哨兵位头结点的双向链表。

1.2list的使用
list中的接口比较多,此处类似,只需要掌握如何正确的使用,然后再去深入研究背后的原理,已达到可扩展的能力。以下为list中一些常见的重要接口。
- 构造

- 迭代器
此处,大家可暂时将迭代器理解成一个指针,该指针指向list中的某个节点。


需要注意的是list的不支持下标+[]。
因为之前像vector这样底层连续的空间。直接用+指针下标即可索引到目标值。
但是list空间不连续。所以需要遍历n次依次移动才可以。所以时间复杂度是O(n).
语法上可以实现,但是可行性上成本很高所以list没有实现operator[]。
【注意】
- begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
- rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动
这就是list的迭代器。
-
list capacity

-
list element access

-
list modifiers

-
emplace_back
emplace_back这个接口和push_back差不多。

只是emplace_back支持传构造自定义对象的参数。
所以这种场景下emplace_back更高效。少了依次拷贝构造
- splice
这里接口的主要功能是将一个链表的节点转移到另一个链表。
也可以调整当前链表的节点顺序。

转移链表的节点
调整当前链表

1.3迭代器划分
根据功能和性质可以将迭代器这样划分

而迭代器的底层结构决定了他的性质。
例如vector底层结构连续就可支持随机访问。
同时迭代器的性质决定了可以使用那些算法。
例如算法库的sort算法就要求是随机迭代器。

因为他的底层是快排。需要支持随机访问和下标±[]。
所以list不可以库的sort,因为他不支持随机迭代器。
所以list自己支持了一个sort

同时C++库里对迭代器的定义使用了继承的概念。子类就是特殊的父类

如果我们想查看某个容器的迭代器就去官网查看即可。

二.list的模拟实现
list因为底层空间不连续所以迭代器我们要用模版封装起来 节点也用模版封装起来
list也是模版封装。

2.1 list结构的定义
template<class T>
struct list_node
{T data;list_node<T>* prev;list_node<T>* next;list_node(const T& x=T()):data(x),next(nullptr),prev(nullptr){}
};
template<class T,class Ref,class Ptr>
struct list_iterator
{typedef list_node<T> Node;typedef list_iterator<T,Ref,Ptr> self;
};
template<class T>
class list
{typedef list_node<T> Node;
public:typedef list_iterator<T,T&,T*> iterator;typedef list_iterator<T,const T&,const T*> const_iterator;private:Node* _head;size_t _size;
};
2.2iterator迭代器
- operator±
operator±我们只需要手动让迭代器指向前后位置即可。
self& operator++()
{self tmp(*this);_node=_node->next;return *this;
}
self& operator--()
{_node = _node->prev;return *this;
}
- operator* 和operator->
operator*就直接返回data即可。
这里为了支持T是自定义类型指针访问数据。
我们需要支持operator->取出节点data的地址。
同时为了防止const_iterator和iterator两个类模版冗余,
并且这两个模版只有这两个函数的返回值不同。
*返回T& ->返回T *
我们让list和list_iterator多加两个模版参数,让这两个模版参数做返回值即可。
Ref operator*()
{return _node->data;
}
Ptr operator->()
{return &_node->data;
}

需要注意的是如果T是自定义类型
那it->a1这里是省略了一个->.

- operator++ - -
这里我们自己手动移动_node即可。
如果后置++ - -我们先拷贝一份当前位置再移动即可。
self& operator++()
{_node = _node->next;return *this;
}
self& operator--()
{_node = _node->prev;return *this;
}
self& operator--(int)
{self tmp(*this);_node = _node->prev;return tmp;
}
self& operator++(int)
{self tmp(*this);_node=_node->next;return *this;
}
这里return *this会调用迭代器的拷贝构造。
但是我们不用写。因为编译器会自动生成一份浅拷贝的拷贝构造。
那浅拷贝会不会有问题呢?
不会,因为我们要的就是浅拷贝。

- operator!=和operator==
operator!=和operator==直接判断指针是否相等即可。
bool operator!=(const self& s) const
{return _node != s._node;
}
bool operator==(const self& s) const
{return _node == s._node;
}
2.3list迭代器
迭代器我们直接返回哨兵位的下一个数据节点和哨兵位节点即可
iterator begin()
{return _head->next;
}
iterator end()
{return _head;
}
const_iterator begin() const
{return _head->next;
}
const_iterator end() const
{return _head;
}
2.3构造
我们先写一个初始化链表的函数。
new一个哨兵位节点。让节点的前后指针都指向自己即可。
void empty_init()
{_head = new Node;_head->next = _head;_head->prev = _head;_size = 0;
}
list()
{empty_init();
}
2.4拷贝构造
拷贝构造我们先初始化链表,然后把链表节点尾插到新链表即可
list(initializer_list<T> il)
{empty_init();for (auto& x : il){push_back(x);}
}
2.5 operator=
这里我们让先拷贝一份形参list,然后交换list和*this即可。函数结束后list自动销毁。
void swap(list<T> list)
{std::swap(_head, list._head);std::swap(_size, list._size);
}
list<T>& operator=(list<T> list)
{swap(list);return *this;
}

2.6 insert
insert要在某个位置插入数据。
我们new开一个节点,将前后节点和newnode连接起来即可。
iterator insert(iterator pos, const T& x)
{Node* cur = pos._node;Node* prev = cur->prev;Node* newnode = new Node(x);//prev newnode curnewnode->next = cur;newnode->prev = prev;prev->next = newnode;cur->prev = newnode;++_size;return newnode;
}
2.7 erase
erase连接前后节点。释放pos位置的节点即可。
同时防止释放哨兵位的头结点。
iterator erase(iterator pos)
{assert(pos != end());//防止销毁哨兵位的头结点Node* next = pos._node->next;Node* prev = pos._node->prev;next->prev = prev;prev->next = next;delete pos._node;//释放原节点--_size;return next;
}
2.8 clear
clear只需要从begin开始erase节点即可。注意erase后迭代器失效,要接受返回值也就是下一个位置的迭代器。
void clear()
{auto it = begin();while (it != end()){it = erase(it);}
}
2.9析构函数
复用clear清空节点后,释放哨兵位节点,再把_head置空即可。
~list()
{clear();delete _head;_head = nullptr;
}
三.按需实例化
编译器对模版是按需实例化。
调用了才会实例化,不调用就不会实例化。

四.迭代器失效
-
insert
list的insert不会导致迭代器失效。


-
erase
erase迭代器失效

五.initializer_list
C++11中,list还可以这样初始化。
用一个花括号括起来,里面就是初始化的内容。

这主要与initializer_list类型有关。C++11标准库之后可以认为花括号括起来的类型就是initializer_list。


它的底层在栈上开一个数组把初始化内容存起来,initializer_list对象有两个指针。
一个指针指向数据的开始,一个指针指向结束。

它支持这几个成员。

迭代器支持只读

所以我们要支持一个initializer_list的list构造即可实现花括号初始化。
list(initializer_list<T> il){empty_init();for (auto& x : il)//支持迭代器就是支持范围for{push_back(x);}}
支持迭代器就支持范围for.
将initializer_list存储的数据拿来尾插即可。
后言
这就是list结构剖析及其模拟实现。大家自己好好消化。今天就分享到这,感谢各位的耐心垂阅!咱们下期见!拜拜~

相关文章:
【C++笔记】list结构剖析及其模拟实现
【C笔记】list结构剖析及其模拟实现 🔥个人主页:大白的编程日记 🔥专栏:C笔记 文章目录 【C笔记】list结构剖析及其模拟实现前言一 .list的结构及其介绍1.1list的结构1.2list的使用1.3迭代器划分 二.list的模拟实现2.1 list结构…...
C#进阶1
C#进阶1 本文章主要介绍C#的进阶知识,如反射,特性.... 参考视频链接 原码 文章目录 C#进阶1反射步骤泛型反射调用方法 获取属性 特性特性的定义步骤扩展枚举练习 反射 在 C# 中,反射(Reflection)是一种强大的机制&a…...
PHP如何对输出进行转义
在PHP中,对输出进行转义是为了防止跨站脚本攻击(XSS)和其他安全问题。PHP提供了多种函数来对输出进行转义,这些函数根据输出的上下文(如HTML、JavaScript、URL等)而有所不同。以下是一些常用的转义函数及其…...
Windows 10 安装Docker踩过的坑和解决-31/10/2024
目录 环境版本 一、Docker Desktop双击启动没反应,open //./pipe/dockerDesktopLinuxEngine: The system cannot find the file specified. 二、Docker Desktop运行run命令时显示错误HTTP code 500 并且错误大意是服务器拒绝访问 三、检测Docker是否可以正常使用…...
【应急响应】Linux植入恶意程序排查流程
文章目录 前言一、Linux入侵检查二、Linux系统被入侵/中毒有哪些现象三、Linux系统被入侵/中毒处置过程四、Linux安全防护措施五、服务器被GetShell渗透解决办法(案例)前言 本篇文章主要是以我们日常的运维工作中对Linux服务器进行安全检查,进一步介绍如何使用具体命令来对Li…...
微信小程序app.js里面onLaunch里面的函数比page里面的onshow里面的方法后执行
微信小程序app.js里面onLaunch里面的函数比page里面的onshow里面的方法后执行 我们在app.js里面执行登录时可以调用checkLoginReadyCallback wx.login({ success: (res) > { $api .login({ jsCode: res.code, }) .then((res1) > { wx.hideLoading(); if (res1.code 0) …...
斐波那契时间序列,精准捕捉市场拐点 MT4免费公式源码!
指标名称:斐波那契时间序列 版本:MT4 ver. 2.01 斐波那契时间序列是一种技术分析工具,通过将斐波那契数列(如1, 2, 3, 5, 8, 13等)应用于时间轴上,用于预测市场价格的时间周期拐点。斐波那契时间序列在股…...
计算机的错误计算(一百四十)
摘要 探讨 MATLAB 中函数 的计算精度。 从计算机的错误计算(一百三十九)知,对于对数运算,当真数在 1 附近时,计算机的输出会出现较大误差。为此,IEEE 754-2019 中专门定义有函数 其目的就是当自变量在 …...
JavaEE初阶---网络原理(四)--IP协议/DNS协议
文章目录 1.初识网络层(了解即可)2.地址管理2.1动态分配2.2网络地址转换2.3IP-v6最终解 3.网段划分4.以太网协议--数据链路层5.DNS应用层协议 1.初识网络层(了解即可) 网络层做的事情就是下面的两个: 1)地…...
LeetCode20:有效的括号
原题地址:. - 力扣(LeetCode) 题目描述 给定一个只包括 (,),{,},[,] 的字符串 s ,判断字符串是否有效。 有效字符串需满足: 左括号必须用相同类型的右括号闭合…...
简单介绍Class文件、Dex文件以及ELF文件
Class文件 Class文件是Java源代码文件经Java编译器编译后得到的Java字节码文件。对比Linux、Windows上的可执行文件而言,Class文件可以看作是Java虚拟机的可执行文件。 Dex文件 Dex文件是Android平台上与传统Class文件对应的Java字节码文件。Dex文件的核心内容与Cl…...
Vivo开奖了,劝退价。。
vivo 也开奖了,不过有小伙伴反馈是个劝退价,甚至不如隔壁的 oppo,要说这两家也是渊源颇深,一家是绿厂,一家是蓝厂,高管也都是早期步步高出来的。 给大家盘一下开奖的信息,方便大家横向做个对比&…...
鸿蒙打包hvigorw clean报错No npmrc file is matched in the current user folder解决
问题 在执行hvigorw clean等命令时,报错如下: Error: The hvigor depends on the npmrc file. No npmrc file is matched in the current user folder. Configure the npmrc file first解决方案 在用户当前目录下新建.npmrc文件,并配置如下…...
无人机救援系统基本组成
无人机救援系统基本组成 1. 源由2. 组成2.1 无人机载具2.1.1 多旋翼2.1.2 垂起固定翼2.1.3 智能避障2.1.4 物资投递 2.2 智能吊舱2.2.1 云台2.2.2 高清摄像2.2.3 红外热成像2.2.4 激光测距2.2.5 目标跟踪 2.3 通讯链路2.3.1 超长距离通信2.3.2 长距离通信2.3.3 中等距离通信 2.…...
git入门教程
git入门教程1:git简介git入门教程2:git发展历史git入门教程3:安装配置git入门教程4:git工作流程git入门教程5:git仓库操作git入门教程6:git基本版本控制git入门教程7:git与远程仓库的交互git入门…...
AMBA:AHB_Slave_Mux的解析与HREADY、HREADYOUT
相关阅读 AMBAhttps://blog.csdn.net/weixin_45791458/category_12800219.html?spm1001.2014.3001.5482 简介 从1999年的AMBA2发布以来,AHB协议中就存在数据选择器,如图1所示的AHB2协议的总线互连。 图1 AHB2的总线互连 这幅图画得比较粗糙࿰…...
初始Linux (2) : 权限
1. su [用户名]及权限概念 Linux中有两种用户:普通用户、超级用户 超级用户可以再 linux 系统下做任何事情,不受限制;而普通用户只能做有限的事情。 可以使用指令:su -快速进入root账户,但需要输入相关密码。 超级用…...
在Mac下安装时间序列软件Hector
1.软件介绍 Hector 是一款开源软件,专用于 GNSS 时间序列数据的处理与分析,广泛应用于地球科学研究。它帮助研究人员从 GNSS 数据中提取长期趋势、周期性成分,并建模噪声特性,用于地壳形变、地震影响和气候变化等方面的研究。Hec…...
JVM1.8内存模型
一、内存模型概览 本文介绍的是JDK1.8的内存模型。1.8同1.7相比,最大的差别就是元空间取代了永久代。元空间的本质和永久代类似,都是堆JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不存在虚拟机中,而是…...
windows C#-类型系统(上)
C# 是一种强类型语言。 每个变量和常量都有一个类型,每个求值的表达式也是如此。 每个方法声明都为每个输入参数和返回值指定名称、类型和种类(值、引用或输出)。 .NET 类库定义了内置数值类型和表示各种构造的复杂类型。 其中包括文件系统、网络连接、对象的集合和…...
终极Java数据结构指南:从链表到红黑树的实现与原理
终极Java数据结构指南:从链表到红黑树的实现与原理 【免费下载链接】CodeGuide :books: 本代码库是作者小傅哥多年从事一线互联网 Java 开发的学习历程技术汇总,旨在为大家提供一个清晰详细的学习教程,侧重点更倾向编写Java核心内容。如果本仓…...
2026程序员必看:收藏这份AI大模型学习资源包,小白也能轻松入门!
2026程序员必看:收藏这份AI大模型学习资源包,小白也能轻松入门! 随着AI大模型技术的快速发展,传统编程技能已难以满足职场需求。本文分析了程序员面临的职场焦虑,指出掌握大模型技术是2026年程序员提升竞争力的关键。文…...
第9课:Linux开发工具(四):make与makefile
第9课:Linux开发工具(四):make与makefile 一、为什么我们需要 Makefile? 1.1 IDE 背后的秘密 在使用 Visual Studio 等 IDE 时,我们只需按下 F5 或点击"编译"按钮,程序就会自动完成编…...
ARIS:基于技能化工作流的AI自主研究系统设计与实践
1. 项目概述:ARIS,一个让AI在你睡觉时做研究的自主工作流 如果你是一名机器学习或计算机科学领域的研究者,我猜你肯定有过这样的体验:一个绝妙的想法在深夜闪现,你兴奋地爬起来记下几行潦草的笔记,然后第二…...
EASY-HWID-SPOOFER:保护数字身份的Windows硬件伪装利器
EASY-HWID-SPOOFER:保护数字身份的Windows硬件伪装利器 【免费下载链接】EASY-HWID-SPOOFER 基于内核模式的硬件信息欺骗工具 项目地址: https://gitcode.com/gh_mirrors/ea/EASY-HWID-SPOOFER 在数字世界中,您的硬件设备就像指纹一样独一无二。操…...
RISC-V处理器架构演进:从单周期到流水线的性能跃迁之路
1. 从单周期到流水线:RISC-V架构的性能进化史 第一次接触处理器设计时,我盯着单周期架构的电路图看了整整三天。最让我困惑的是:为什么简单的加法指令要和复杂的访存指令共用相同的时钟周期?这个问题背后,藏着处理器架…...
SpringBatch学习
/*** 示例一:Tasklet 方式*/ Configuration EnableBatchProcessing public class TaskletBatchConfig {private static final Logger logger LoggerFactory.getLogger(TaskletBatchConfig.class);Autowiredprivate JobBuilderFactory jobBuilderFactory;Autowiredp…...
别再对着示波器数NOP了!用STM32的SPI+DMA驱动WS2812灯带,一个CubeMX配置就搞定
用STM32的SPIDMA高效驱动WS2812灯带:告别手动调时序的工程化方案 在嵌入式开发中,驱动WS2812灯带一直是个让人又爱又恨的挑战。这种智能RGB灯带以其简单的单线控制和丰富的色彩表现广受欢迎,但精确的时序要求也让不少开发者头疼不已。传统方法…...
解决企业级日期处理难题:Vue3-DateTime-Picker的现代化架构设计与实战应用
解决企业级日期处理难题:Vue3-DateTime-Picker的现代化架构设计与实战应用 【免费下载链接】vue3-date-time-picker Datepicker component for Vue 3 项目地址: https://gitcode.com/gh_mirrors/vu/vue3-date-time-picker Vue3-DateTime-Picker是一款基于Vue…...
超自动化巡检:如何应对海量增长的基础设施?
在数字化转型的浪潮中,企业IT基础设施正经历着前所未有的指数级增长。从物理服务器到虚拟机,从容器集群到云原生环境,从传统数据中心到边缘节点,运维对象的数量与种类正在以几何级数膨胀。某大型企业单日告警量可达130万条&#x…...

