list类底层逻辑实现

news2025/1/16 10:54:36

  list的底层逻辑是一个双向带头链表。那么list的底层其实就跟我们之前实现的带头双向链表相同,都是开辟一个一个单独的节点,最后再通过指针将各个单独的节点链接起来即可。

  我们来类比之前编写的双向带头链表实现具体的内容。

  创建一个list类的主体

  

  就像我们说的那样,我们需要先创建一个带头的空链表。在编写list的构造函数之前我们需要先实现一个结构体,一个用于创建节点的结构体。之后我们可以直接new一个该节点直接进行链接操作即可。

  

  我们创建一个结构体之后就可以在里面再编写一个构造函数,用于初始化我们节点的内容,在创建的同时可以对节点进行初始化操作。

  那么之后我们就可以进行编写list的构造函数了:

  我们在默认构造当中进行的操作就是开辟好一个头节点,并将该节点单独链接成为带头双向循环链表。

  push_back()尾插函数

  在初始化好链表之后我们就可以进行链表功能的正式编写了。其实插入操作也非常的简单。我们只需要新创建一个节点,并将该节点链接进入带头双向循环链表当中即可。

  我们仔细梳理逻辑可以发现:想要实现尾插操作只需要在头节点之前链接进入新的节点即可。因为我们的头节点的前一个位置就是我们规定链表的最后一个位置。编写代码如下:

  iterator类

  之后我们就会想要测试该函数是否可以正常的运行。所以我们就需要一个可以打印出链表当中元素的函数。所以我们下一步就需要实现我们的迭代器的功能。

  但是我们会发现,list的迭代器跟我们之前编写的vector和string的迭代器有很大程度上的区别。之前编写的迭代器都是顺序存储结构,我们可以使用地址的指针作为迭代器直接使用。但是list是链式存储结构。那么就不能直接使用地址作为迭代器了,所以我们封装了一个新的类作为迭代器。

  在这个迭代器当中我们创建了一个新的node结点用于在链表当中进行移动。(因为我们原有的类list类当中_head指针不能进行改变,所以就需要一个新的node指针适应我们的++操作。)之后我们只需要实现相应的构造函数即可。

  仔细看的话大家肯定会对iterator的构造函数有疑问,这不是进行浅拷贝了吗?那么在释放的时候会不会有歧义呢?在这里确实进行了浅拷贝操作,但是我们想要的就是浅拷贝的效果。因为我们迭代器想要指向的就是我们已经开辟好的地址的指针,所以进行浅拷贝才是我们想要的效果。

  我们可以通过使用库当中的迭代器打印输出链表数据的形式进行反向推导我们想要实现的相关的功能。

    首先我们需要实现的就是begin和end函数。我们需要返回一个迭代器类型的数据。

  让我们进行分析一下:我们只需要返回_head指针的下一个位置即可,因为_head指针的下一个位置表示的是链表当中第一个元素节点的指针,作为返回值我们系统会自动调用iterator的构造函数,参数类型正好相同直接会生成一个对应的_node节点,也就是我们作为变量持续进行循环的对象。

  同样的end函数也是这个道理。我们只需要返回_head指针即可。

  之后我们需要实现的功能就变成了三处:1.实现 !=  2.实现 *  3.实现 ++

  运算符重载*

  对于*的运算符重载函数其实很简单,我们想要的就是打印输出链表当中的数据内容,所以我们只需要返回链表当中存储的数据即可。也就是我们迭代器当中保存的节点_node当中所对应的值。

  运算符重载!=

  我们可以思考一下不等号两边的数据类型。

  仔细观察我们就会发现,不等号两边都是我们重载之后的iterator的类型,而我们想要比较的是迭代器当中保存的节点的指针。所以我们可以直接写出如下的代码:

  第一个_node是我们的左操作数tmp当中的成员变量,我们可以直接使用,而我们的右操作数是一个iterator的类型的数据,所以我们就需要通过 . 操作符找到当中的成员变量。这两个参数就是我们想要比较的对象,我们直接比较并返回比较之后的结果即可。

  运算符重载++

  对于++的运算符重载函数同样很简单,我们只需要修改iterator当中的node节点当中保存的数据即可。也就是将_node指针指向下一个位置即可。

  

  在实现上述的函数之后,我们就可以打印输出链表当中相应的数据了。

  iterator的模板参数

  T&和const T&

  当我们在使用链表进行数据输出的时候很容易会想到:list当中保存的是变量数据,但是如果我们想要保存常量数据,也就是不允许其他人修改链表当中的数据应该怎么做呢?

  我们进行分析可以发现:

  实际上起作用的也就是我们的解引用操作,如果我们对T进行一个限定也就是添加一个const进行修饰不就好了吗?那该如何做呢?直接typedef const list_iterator<T> const_iterator ? 当然不行我们会发现这个const是针对于我们的iterator类并不是仅仅针对于我们的T参数。那么就有人可能会说了:直接复制一份命名为const_list_iteartor,并将模板传承const T不就好了吗?但是我们会发现我们就仅仅修改了一个参数,而我们的代码却变得十分冗余。为什么不直接添加参数呢?

  T*和const T*

  我们只需要添加一个参数进行修改即可。对于不同参数的迭代器类型我们可以命名成不同的形式,之后系统会自动实例化成为相应的类。如果我们调用的是普通的迭代器就会实例化成为T*,如果传入const迭代器就会实例化成为const T*,从而达到我们想要的效果。

  当我们的链表当中保存的是内置类型的数据上述功能已经可以正常使用了,但是如果我们的链表当中保存的是自定义类型的数据呢?我们会不会想到需要重定义一个 -> 方便我们进行查找自定义类型当中的变量呢?

  如果我们不进行重载 -> 操作的时候我们必须进行上述的操作。但是我们如果重载 -> 之后我们就可以通过该操作符直接进行打印操作。

  是不是看起来简洁很多呢?那么我们也将我们的函数进行实现该功能。

  

  实质上就是通过->之后我们可以获得链表节点当中保存的数据的地址,如果该数据是自定义类型我们就可以通过这个指针找到其中的成员变量。

  但是我们会发现有一点奇怪:按照这么说我们应该进行的是两次 -> 呀?事实上确实是两次操作,第一次->是我们的运算符重载,会获得自定义类型的指针,第二次->找到自定义类型当中的成员变量。

  同样的 -> 在使用的时候也会有const的需求,那么我们就可以按照之前的思考,再添加一个参数,用来控制我们的T*是否可以被修改。

  修改代码并运行之后发现结果一切正常。

  insert函数

  实现完成上述代码之后,我们list的大部分逻辑都已经完成了,那么接下来就可以进行相关具体功能性函数的添加操作了。首先我们来实现insert函数。

  我们只需要开辟一个空间,将新开辟的空间链接进入链表当中即可。

  

  erase函数

  对于删除函数,我们只需要取下某个节点,链接前一个节点跟后一个节点即可。但是我们需要注意的是返回值的设置。由于我们的该位置的节点已经释放,所以我们该位置就无法使用,所以为了避免迭代器失效的问题,我们需要主动传出一个返回值,该返回值是我们删除节点的下一个位置。

  

  代码运行一切正常。

  拷贝构造

  之后我们来实现拷贝构造函数。实现拷贝构造函数,首先我们需要获得头节点,由于构造函数都需要执行该功能,所以我们将获取头节点的函数独立封装成为createhead函数方便我们进行复用。

  之后只需要将我们原链表当中的元素依次尾插进入我们的新链表当中即可。

  代码运行一切正常。

  运算符重载=

   对于=的运算符重载同样很简单,我们只需要交换头节点即可。释放的工作完全可以交给析构函数自动进行。

  clear函数

  在完成析构函数之前,我们先完成以下clear函数。该函数的功能是清空链表当中保存的值,但是不释放头节点。我们可以重新进行对链表的操作。

  

  对于该功能的实现,我们直接复用之前实现的erase功能即可。但是需要我们注意的是erase之后我们需要使用it接受删除之后的指针,否则就会产生报错。

  运行结果一切正常。

  析构函数

  析构函数我们直接调用clear函数清空链表当中的节点,最后再释放head节点即可。

  

  退出码为0,代码运行一切正常。

  使用n个相同的参数进行构造

  原理还是相同的,我们只需要初始化头节点之后尾插n个由该数据构造的节点即可。

  

  代码运行一切正常。

  迭代器构造

  首先我们需要进行的是初始化头节点,之后依次拷贝迭代器区间的值即可。

  ​​​​​​​

  同样的当我们使用模板参数初始化迭代器的时候需要重载一份使用n个参数进行构造的构造函数,避免重复,避免适配错误。

    size函数

  这个函数用于返回我们链表当前的元素的个数,我们可以避免麻烦直接创建一个成员变量,当我们对链表当中的数据进行修改的之后产生对应的改变即可。

  代码运行一切正常。

  front函数和back函数

  该函数的功能就是返回链表当中的第一个位置还有最后一个位置。我们分别实现普通版本和const版本。

  ​​​​​​​

  代码运行一切正常。

  头删,头插,尾删,尾插函数

  尾插函数我们已经实现完成了,那么之后我们只需要完成头删头插,尾删操作即可。

  代码运行一切正常。

  重载运算符--

  重载运算符--操作其实跟我们重载运算符++操作很像,我们只需要将移动的指针修改成为prev即可。

  ==运算符重载

  同样的道理其重载过程跟我们的!=完全相同。

  那么到此位置list的底层逻辑实现也就全部完成了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2092622.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一个浏览器插件如何月入12万美元:深入了解 GoFullPage

一个浏览器插件如何月入12万美元&#xff1a;深入了解 GoFullPage 前言 GoFullPage 这个插件的诞生&#xff0c;源于其创作者 Peter Coles 的一个简单想法&#xff1a;解决一个他在日常开发工作中遇到的痛点。早在 2012 年&#xff0c;Coles 发现许多现有的网页截图工具无法完…

Qt 多个按钮,响应同一个点击事件

最近的一个需求&#xff0c;需要多个按钮响应同一个点击事件&#xff0c;并且要求能区分是哪个按钮点击的&#xff0c;看效果&#xff1a; 直接上代码&#xff1a; QList<QPushButton*> buttons findChildren<QPushButton*>();for (QPushButton* button : buttons…

Centos Stream9系统安装及网络配置详解

1.镜像下载 如未拥有系统镜像文件的伙伴可通过前往下面的连接进行下载&#xff0c;下载完成后需将其刻录至U盘中。 PS&#xff1a;该U盘应为空盘&#xff0c;刻录文件会导该盘格式化&#xff0c;下载文件选择dvd1.iso完整包&#xff0c;适用于本地安装。 下载地址&#xff1…

波导阵列天线单元学习笔记7 一种用直接金属激光烧结考虑的轻质量,宽带,双圆极化波导腔体阵列

摘要&#xff1a; 提出了一种工作在Ku频段的轻质量&#xff0c;宽带&#xff0c;双圆极化波导腔体阵列。为了获得双正交的线极化&#xff0c;基本的辐射单元是由两个波导馈电的方形腔体。通过恰当地对馈网进行调谐&#xff0c;可以获得对于两个正交极化的等辐同相辐射电场&…

学习之MySQL约束

概述 1、概念&#xff1a;约束是作用于表中字段上的规则&#xff0c;用于限制存储在表中的数据 2、目的&#xff1a;保证数据库中数据的正确性&#xff0c;有效性和完整性 3、分类&#xff1a; 注意&#xff1a;约束是作用域表中字段上的&#xff0c;可以创建表/修改表的时候…

jenkins安装k8s插件发布服务

1、安装k8s插件 登录 Jenkins&#xff0c;系统管理→ 插件管理 → 搜索 kubernetes&#xff0c;选择第二个 Kubernetes&#xff0c;点击 安装&#xff0c;安装完成后重启 Jenkins 。 2、对接k8s集群、申请k8s凭据 因为 Jenkins 服务器在 kubernetes 集群之外&#xff0c;所以…

华为云征文|部署个人博客管理系统 Ghost

华为云征文&#xff5c;部署个人博客管理系统 Ghost 一、Flexus云服务器X实例介绍1.1 云服务器介绍1.2 应用场景1.3 对比普通ECS 二、Flexus云服务器X实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置 三、部署 Ghost3.1 Ghost 介绍3.2 Docker 环境搭建3.3 Ghost 部署3.4 Gho…

同时学习C++和Java,会如何

在当今信息时代&#xff0c;编程语言如同人类的语言&#xff0c;是沟通机器的桥梁。 而随着技术的不断发展&#xff0c;编程语言种类繁多&#xff0c;选择哪种语言学习成为了许多人面临的难题。 有人建议专注于一门语言&#xff0c;精益求精&#xff0c;而也有人主张同时学习多…

Windows连接虚拟机中的mysql5失败

Windows连接虚拟机中的mysql5失败 虚拟机版本为centos6.8&#xff0c;数据库版本为mysql5.6&#xff0c;系统版本为window11。在虚拟机上安装好mysql&#xff0c;并且配置好权限&#xff0c;虚拟机登录正常之后。在windows11上使用工具dbeaver连接mysql失败&#xff0c;报错 Co…

春秋云镜(ZZCMS 2023)·CVE-2023-50104

漏洞参考说明&#xff1a;GitHub - zzq66/cve4 漏洞复现&#xff1a; 1、访问 URL/3/E_bak5.1/upload/index.php 2、使用默认账户密码admin/admin登录 3、上传恶意语句 修改tablename字段为eval($_POST[1]) POST /3/E_bak5.1/upload/phomebak.php HTTP/1.1 Host: eci-2zehp12…

黄仁勋预言步入现实 谷歌展示实时游戏生成AI模型GameNGen

由AI模型实时生成游戏的时代&#xff0c;已经悄然来到了我们身边。本周来自谷歌公司和特拉维夫大学的研究人员发表了一篇名为《Diffusion模型是实时游戏引擎》的论文&#xff0c;介绍了计算机历史上第一个完全由神经网络模型支持的游戏引擎GameNGen。 研究人员在论文中写道&…

51单片机.之蜂鸣器振动播放歌曲

蜂鸣器发声是通过喇叭振动发声的&#xff0c;通电产生磁场&#xff0c;磁铁吸收&#xff0c;而振动。不断释放&#xff0c;吸收。 1、蜂鸣器发声&#xff0c;播放不同频率的声音逐渐变尖 #include<reg52.h>sbit BUZZ P1^6;unsigned char T0RH0; unsigned char T0RL0; v…

七、性能测试

文章目录 一、常见的性能测试二、为什么要进行性能测试三、性能测试实施的流程&#xff08;一&#xff09;如何确定性能测试的需求1.关键性能指标分析2.关键业务分析 &#xff08;二&#xff09;常见的性能指标 三、性能测试定义四、性能测试关键指标&#xff08;一&#xff09…

论文笔记:Estimating future human trajectories from sparse time series data

sigspatial 2023 humob竞赛paper hiimryo816/humob2023-MOBB (github.com) 1 数据集分析 这里只分享了HuMob数据集1的内容 1.1 假日分析 对HuMob数据集#1地理数据的方差分析显示了非工作日的模式 在某些天的y坐标方差中有显著的峰值&#xff0c;这是非工作日的象征【x坐标…

写给LLM新手的建议,让你少走2年弯路!

大模型的爆火&#xff0c;在全球范围内引发了一场A1“狂也在业界点燃了一场百模大战。结合蕞近飙”&#xff0c;两年我在大模型领域的一些经验&#xff0c;分享一下在校学生/新人如何转到大模型阵营。 1、大模型有哪些从业方向? 首先你可以在求职网站搜索一下“大模型关键词&…

重卡智能充电机器人

产品介绍 随着新能源电池行业的发展&#xff0c;越来越多码头、矿山等场景的重型卡车都改为新能源供电形式&#xff0c;由于新能源重卡充电电压为1.2MW&#xff0c;充电口离地1.8m&#xff0c;充电枪充电线缆重量为50kg&#xff0c;人工操作难度大、危险性大&#xff0c;所以我…

arcpy将数据库要素类添加到图层组以及创建要素收藏夹如何创建文件夹并将模板中的符号添加到文件夹中

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

算法基础-位运算

>> &#xff08;右移运算&#xff09; x >> y&#xff1a;表示将x的二进制值右移y位。 正数是直接右移y位&#xff0c;则高位&#xff08;最左边&#xff09;补y个0。 负数是求补码&#xff0c;然后右移y位&#xff0c;最高位补y个1&#xff0c;再求反码&#xff…

react|useState的异步渲染

useState 组件里面的变量可以用state来表示&#xff0c;setState函数是用来更新state的值的用法 let [age,setAge]useState(0); // 0是变量age的初始值异步渲染 setState是异步指定的。也就是setAge是异步执行的。执行但不是立刻渲染&#xff0c;而是进入到微任务队列。注意…

数值分析笔记(六)非线性方程求根

非线性方程求根 二分法 二分法是线性收敛的。 不动点 对于非线性方程 f ( x ) 0 f(x)0 f(x)0&#xff0c;将其转化为 x φ ( x ) x\varphi(x) xφ(x)&#xff0c;若 x ∗ x^* x∗满足 f ( x ∗ ) 0 f(x^*)0 f(x∗)0&#xff0c;称 x ∗ x^* x∗为 φ ( x ) \varphi(x) φ…