【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 类库定义了内置数值类型和表示各种构造的复杂类型。 其中包括文件系统、网络连接、对象的集合和…...
Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...
智能AI电话机器人系统的识别能力现状与发展水平
一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
破解路内监管盲区:免布线低位视频桩重塑停车管理新标准
城市路内停车管理常因行道树遮挡、高位设备盲区等问题,导致车牌识别率低、逃费率高,传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法,正成为破局关键。该设备安装于车位侧方0.5-0.7米高度,直接规避树枝遮…...
多元隐函数 偏导公式
我们来推导隐函数 z z ( x , y ) z z(x, y) zz(x,y) 的偏导公式,给定一个隐函数关系: F ( x , y , z ( x , y ) ) 0 F(x, y, z(x, y)) 0 F(x,y,z(x,y))0 🧠 目标: 求 ∂ z ∂ x \frac{\partial z}{\partial x} ∂x∂z、 …...
WEB3全栈开发——面试专业技能点P4数据库
一、mysql2 原生驱动及其连接机制 概念介绍 mysql2 是 Node.js 环境中广泛使用的 MySQL 客户端库,基于 mysql 库改进而来,具有更好的性能、Promise 支持、流式查询、二进制数据处理能力等。 主要特点: 支持 Promise / async-await…...

