【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 类库定义了内置数值类型和表示各种构造的复杂类型。 其中包括文件系统、网络连接、对象的集合和…...

【酷狗音乐】逆向登录参数分析
mid、uuid参数 从cookie里面取值kg_mid,没有就生成 dfid也是从cookie里面取的kg_dfid 清空cookie dfid "-"也是可以的 md5加密了一个随机uuid import uuid import hashlibuuid1 str(uuid.uuid4())def md5_encrypt(text):return hashlib.md5(text.enco…...

Jenkins面试整理-Jenkins Pipeline 是什么?
Jenkins Pipeline 是一种将 Jenkins 中的持续集成和持续交付(CI/CD)流程定义为代码的方式。Pipeline 提供了一种灵活、可维护的方式,通过脚本来描述构建、测试、部署等流程。Jenkins Pipeline 使用 Groovy 作为脚本语言,并可以通过 Jenkinsfile 来定义和管理流水线。 Jenki…...

RHCE第三次实验
要求 (1)学生信息网站只有song和tian两人可以访问,其他用户不能访问。 (2)访问缴费网站实现数据加密基于https访问。 架设一台NFS服务器,并按照以下要求配置 1、开放/nfs/shared目录,供所…...

基于LORA的一主多从监测系统_4G模块上巴法云
临时添加一个更新,更换云平台为巴法云,事情的起因是因为阿里云这个老六,早上睡了一觉起来发短信告诉我云平台给我停了,得交钱,好嘛,不过也没办法现在这基本都收费,当然还有onenet可以用…...

pip使用
pip全称pip install package,是python第三方包sitepackage管理的工具,安装,卸载第三方包。安装python时可以选择安装pip,或自己安装pip 查看pip是否安装:pip --version 安装pip :pip python -m pip install --upgrade…...

Django ORM详解:外键使用(外键逻辑关联)与查询优化
Django数据库迁移 # 创建迁移 python manage.py makemigrations your_app_name # 应用迁移 python manage.py migrate # 查看迁移状态 python manage.py showmigrations # 回滚迁移 python manage.py migrate your_app_name 0001 # 修改表后,删除迁移记录和表删除迁移记录后重…...

【Python】实战:使用input()从键盘获取一个字符串,判断这个字符串在列表中是否存在(函数体不能使用in),返回结果为True或False
使用input()从键盘获取一个字符串,判断这个字符串在列表中是否存在(函数体不能使用in),返回结果为True或False def exists_in_list(input_string, str_list):# 遍历列表中的每个元素for item in str_list:if item input_string: # 如果当前元素等于输…...

【YApi】接口管理平台
一、简介 YApi 是一个用于前后端开发团队协作的 API 管理平台,帮助团队更加高效地进行 API 接口的设计、测试、文档管理和版本控制等工作。 YApi 主要功能: API 设计和管理:提供 API 设计和文档生成工具,使开发者能够轻松创建、…...

QNAP威联通NAS忘记密码怎么办?
创作立场:原创不易,拒绝搬运~ hello 大家好,我是你们的老伙伴,稳重的大王~ 如题:在使用QNAP 威联通NAS期间,如果忘记密码,怎么去找回密码呢? 每台QNAP 威联通NAS,在机器…...

MySQL FIND_IN_SET 函数详解
文章目录 1. 基本语法2. 使用场景3. 实战示例3.1 基础查询示例3.2 与其他函数结合使用3.3 动态条件查询 4. 性能考虑5. 常见问题和解决方案5.1 大小写敏感问题5.2 空值处理5.3 模糊匹配 6. 总结 1. 基本语法 FIND_IN_SET 函数的基本语法如下: FIND_IN_SET(str, st…...