C++STL-list的简易实现

news2024/9/28 23:31:07

文章目录

  • 1. list的介绍
  • 2. 迭代器的分类
  • 3. list的构造
  • 4. list的实现
    • 4.1 list的基本结构
    • 4.2 list的push_back函数
    • 4.2 list的迭代器
      • 4.2.1 operator- >
      • 4.2.2 const迭代器
    • 4.3 insert函数
    • 4.4 earse函数
    • 4.5 迭代器失效问题
    • 4.6 析构函数
    • 4.7 构造函数
    • 4.8 拷贝构造
      • 1. 传统写法
      • 2. 现代写法
    • 4.9 赋值运算符重载

1. list的介绍

在这里插入图片描述
1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器(时间复杂度O(1)),并且该容器可以前后双向迭代(++/- -)
2. list的底层是带头的双向循环链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代。

2. 迭代器的分类

从实现结构的角度,迭代器在实际中分为3类:
第一种:单向迭代器。只支持++
比如:forward_list,unordered_map,unordered_set

第二种:双向迭代器。支持++和- -
比如:list,map,set

第三种:随机迭代器。支持++,- -,+,-
比如:vector,string,deque
不过在单向迭代器中可以传双向迭代器和随机迭代器,双向迭代器可以传随机迭代器

3. list的构造

在这里插入图片描述
这些和我们之前所说的string和vector是差不多的。举个例子演示一下就可以了。
在这里插入图片描述

4. list的实现

4.1 list的基本结构

首先,我们看一下源代码的实现结构:
成员变量:
在这里插入图片描述
成员变量它只有一个link_type类型的节点。
在这里插入图片描述
link_type类型是一个__list_node< T >的模板指针。
__list_node的结构如下:
在这里插入图片描述
这里的指针类型是void*,这里也可以写成__list_node< T > *

那么我们是如何知道它是带头循环双向链表
我们先看一下它的构造函数:
在这里插入图片描述
它在无参构造里调用了empty_initialize()函数:
在这里插入图片描述
这个初始化就是获得一个节点,节点的next指向自己,节点的prev也指向自己。所以它初始化是一个头节点。
我们再看一下它的插入函数:
在这里插入图片描述
从这两行可以看出它是一个双向循环链表。
下面我们就把这个基本结构写一下:
在这里插入图片描述
在这里插入图片描述

4.2 list的push_back函数

带头的双向循环链表我们以前的文章也说过,很简单,直接上代码:
在这里插入图片描述

4.2 list的迭代器

既然是带头的双向循环指针,那么它的结构是这样的:
在这里插入图片描述
我们知道迭代的begin()指向第一个数据,end()指向数据最后一个元素的下一个位置(也就是哨兵位头节点)。
在这里插入图片描述
那么我们看一下标准库里是如何使用的:
在这里插入图片描述
可以看到这个迭代器可以++,解引用时就可以打印数据。那么我们想一下,如果list的迭代器是指针,它是指向这个节点的指针,是如何解引用就拿到数据的呢?所以list的迭代器不是一个指针了。而是封装成了一个类
在这里插入图片描述
下面我们就需要把迭代器的解引用,++和不等于,这三个运算符重载写出来。
在这里插入图片描述
解引用很简单,直接返回节点的数据就行了。
在这里插入图片描述
不等于就是看节点地址是否相同。
在这里插入图片描述
前置++,返回++后的迭代器。

然后,我们要在list类里定义迭代器的begin和end:
在这里插入图片描述
这里我们把迭代器模板重命名,这样用户用迭代器时就不需要关心底层了。
这里的begin()和end()函数里面写的是匿名对象,然后传值拷贝。
我们也可以这样写:
在这里插入图片描述
是一样的。我们也可以这样写:
在这里插入图片描述
这是单参数的隐式类型转换,可以默认构造一个迭代器。
那么我们这里的迭代器,需不需要写析构呢
答案:不需要。因为节点不属于迭代器,不需要迭代器释放。不过编译器会默认生成一个,但什么事情都不干。

那么这里的迭代器,需不需要写拷贝构造和赋值重载呢
答案:不需要。默认生成的就行了,浅拷贝就可以。因为要指向同一块空间。

那么我们现在就来验证一下,看一看写的对不对:
在这里插入图片描述
是可以使用的。

然后,我们再来完善一下它的后置++和前置,后置- -,和==
在这里插入图片描述
在这里插入图片描述

4.2.1 operator- >

我们要知道,迭代器的作用是模仿指针的功能,我们先看下面的代码:
在这里插入图片描述
指针可以使用箭头找到自定义类型的成员,那么迭代器这里也应该可以。
我们就需要重载。
在这里插入图片描述
有同学对这样的代码感觉很疑惑。返回的是一个数据的地址。
在这里插入图片描述
如下图所示:
在这里插入图片描述
但是AA也是一个自定义类型,所以我们还应该加一个箭头,这样写:
在这里插入图片描述
但是不对,这样写就对了:
在这里插入图片描述
这是因为编译器为了可读性进行优化处理,优化以后,省略了一个

4.2.2 const迭代器

在这里插入图片描述
在这里,我们要调用一个打印函数,但是它是被const修饰的。所以我们调用普通的迭代器就不行了。那么我们改成这样呢?
在这里插入图片描述
虽然可以运行了,但是返回的迭代器是可以修改的。但我们是一个const的迭代器,只能读,不能写。这样就违反意愿了。
在这里插入图片描述
那么我们再修改一下:
在这里插入图片描述
在这里插入图片描述
这样虽然不能修改了,但是普通的迭代器也不能被修改了。
在这里插入图片描述
那么我们此时该怎么办呢?大佬们是这样做的:
在这里插入图片描述
我们给这个迭代器的模板添加了两个参数。然后,我们就可以用一个模板实例化两个不同的迭代器类型。
在这里插入图片描述
然后我们将这个类型typedef一下,因为类型有点长了,下面改起来麻烦。
在这里插入图片描述
在这里插入图片描述

在list模板类里面,我们把普通迭代器和const迭代器都重定义一下:
在这里插入图片描述
这样调用的时候会选择最匹配的一个:
在这里插入图片描述
我们看一下匹配的过程,如下图所示:
在这里插入图片描述
普通迭代器的Ref是T&,Ptr是T*
在这里插入图片描述
const迭代器的Ref是const T&,Ptr是const T*

为什么这里第一个模板参数T,不加const呢
在这里插入图片描述
如果我们这里加上const,那么我们看一下这里:
在这里插入图片描述
我们传递的是_head- >_next,它的类型是:list_node< int > *,但是我们想构造一个const迭代器,我们看这里:
在这里插入图片描述
这里的node类型是list_node< const int > *,所以和我们传的就不匹配了,就会出现错误。

4.3 insert函数

在这里插入图片描述
这样push_back和push_front就可以复用了。
在这里插入图片描述
尾插就在end()处插入就行了,头插就在begin()处插入。
在这里插入图片描述

4.4 earse函数

在这里插入图片描述
头删也很简单就是begin(),尾删只需要将end迭代器减减一下:
在这里插入图片描述

4.5 迭代器失效问题

我们先看下面的代码:
在这里插入图片描述
这个代码是将链表中的偶数删除,如果不是的就it++,我们看一下运行结果:
在这里插入图片描述
我们看到并没有进行正确的删除,原因如下:
在这里插入图片描述
如果此时我们将2删除了,那么it就变成了一个野指针。在源码中它是这样做的:
在这里插入图片描述
它是加了一个迭代器的返回值。这个返回值的意思是:指向函数调用擦除的最后一个元素后面的元素的迭代器。如果擦除了序列中的最后一个元素,则容器结束。
在这里插入图片描述

在这里插入图片描述
那么有了返回值,我们就可以这样写:
在这里插入图片描述
验证一下:
在这里插入图片描述
那么insert呢?因为insert插入,它不会扩容,插入进去迭代器还是指向原来的节点,没有发生改变。所以insert不会发生迭代器失效的问题。但是在源代码里,还是给它加了返回值。
在这里插入图片描述
它的返回值是:指向第一个新插入元素的迭代器
在这里插入图片描述
在这里插入图片描述

4.6 析构函数

我们先写一个clear函数:
在这里插入图片描述
这个函数的意思就是:从列表容器中删除所有元素(这些元素将被销毁),并使容器的大小为0。
在这里插入图片描述
实现如下:
在这里插入图片描述
现在我们再来写析构函数:
在这里插入图片描述

4.7 构造函数

在list里还有一个用迭代器来构造的函数:
在这里插入图片描述
这个很容易理解,就是你输入的迭代器区间来构造这个list对象。
在这里插入图片描述
在构造之前,我们肯定要把哨兵位的头节点先弄出来。在源代码里是封装了一个函数来弄的:
在这里插入图片描述
现在,我们再构造就很简单了:
在这里插入图片描述

4.8 拷贝构造

在这里,如果我们不写,编译器会默认构造一个拷贝构造,但是是浅拷贝:
在这里插入图片描述
两个头指针(_head)指向同一块空间。如果析构的话就会析构两次。
所以名为要写深拷贝。

1. 传统写法

在这里插入图片描述
这个意思就是自己从lt里面取数据,然后去插入构造。

2. 现代写法

在这里插入图片描述
现代写法就是先构造一个tmp,然后交换两者的头指针。

4.9 赋值运算符重载

在这里插入图片描述
赋值运算符也是一样的道理,在传参数时就拷贝构造好,然后直接交换头指针就行。

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

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

相关文章

【C++升级之路】第四篇:类和对象(下)

&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f; &#x1f36d;&#x1f36d;系列专栏&#xff1a;【C学习与应用】 ✒️✒️本篇内容&#xff1a;类与对象知识汇总&#xff0c;包括初始化列表的基本概念和使用的注意事项、explicit关键字、C/C中的static成…

Python【方法和返回值(Union)联合类型】注解

什么是类型注解&#xff1a;供调用者在使用函数&#xff08;方法&#xff09;时&#xff0c;如果没有完善的文档作为参考&#xff0c;开发者不知道要给定义的【变量、方法中的函数、】传入什么数据类型&#xff0c;以免减少编译错误。有了类型注解可以让 IDE 知道了数据类型后&…

C++语法2——for、while、do-while的语法及区别

C语言语法1详情请看这两篇博客&#xff1a;&#xff08;此号为本人小号&#xff09; 四则运算及基本语法 数据类型 接下来要讲得是循环语句 for循环 基本语法&#xff1a; for(表达式1&#xff1b;表达式2&#xff1b;表达式3) {内嵌语句&#xff1b; }执行顺序&#xff1a;…

js如何计算年龄?如何创建Javascript 年龄计算器?

如何创建年龄计算器? 要构建这个项目,我们需要HTML,CSS和Javascript。 让我们来看看这个项目是如何工作的。项目由输入日期组成。用户必须单击它并选择他们的出生日期或任何所需的日期。在此之后,用户必须单击计算按钮。用户单击计算按钮后,我们会根据他们输入的日期(…

深入理解ConcurrentLinkedQueue源码

1. 概述 在我们的日常开发中&#xff0c;经常会使用队列这种数据结构&#xff0c;需要它的队尾进、队头出的特点。于是&#xff0c;Doug Lea大师设计了一个线程安全的队列ConcurrentLinkedQueue&#xff0c;它是采用链表的形式构成的。我们接下来尝试通过代码去了解其中的设计…

S7-200SMART通过表格指令实现模拟量信号滑动平均值滤波的具体方法

S7-200SMART通过表格指令实现模拟量信号滑动平均值滤波的具体方法 当现场的模拟量信号波动太大,而通过硬件的方式尚无法实现平稳的信号输入时,可采用软件上的滤波进行信号处理, 本次和大家分享的即通过取多个信号值的平均值的方式实现模拟量滤波的具体方法示例,仅供大家参考…

DataNode节点下线速度优化

目录 一、节点掉线或退役 1.1区分节点掉线和节点退役的区别 1.2 如何处理节点掉线出现的各种风暴 1.2.1 Datanode的block复制 1.2.2 控制节点掉线RPC风暴的参数 二、如何快速节点下线 一、节点掉线或退役 背景&#xff1a;5台数据节点&#xff0c;存储40T数据 block数112…

高等数学(上) —— 一元积分学

文章目录Ch4.不定积分原函数F(x)F(x)F(x)原函数存在定理不定积分∫f(x)dx\int f(x)dx∫f(x)dx不定积分公式不定积分 ⇦⇨ 变上限积分&#xff1a;∫f(x)dx∫0xf(t)dt\int f(x){\rm d}x\int_0^xf(t){\rm d}t∫f(x)dx∫0x​f(t)dtCh5.定积分1.定积分定义定积分的几何意义2.定积分…

ESP32的python开发环境搭建:Thonny+MicroPython

1 Thonny安装 Thonny —— 一个面向初学者的 Python IDE。Thonny良好的支持Microbit、ESP32和树莓派等的开发. 安装下载地址&#xff1a; https://thonny.org/ 2 Micropython安装 MicroPython 是 Python 3 语言的精简实现 &#xff0c;包括Python标准库的一小部分&#xff0…

Vector - VT System - 板卡_VT8006/VT8012

由于最近不幸变为了小*人&#xff0c;因此断更了一周&#xff0c;今天稍有好转&#xff0c;就新加一块大家应该会比较感兴趣的VT板卡硬件介绍吧&#xff0c;也预示着新的开始&#xff0c;马上也要到了元旦&#xff0c;新的一年即将开始&#xff0c;提前在这里祝福大家在新的一年…

JavaPub面试宝典【第22版】

JavaPub面试宝典【第22版】 直接上干货&#xff0c;几百篇原创笔记都在这。 文章列表 &#x1f4da;最少必要面试题 Java基础Java并发入门Java容器JavaWebJVMMySQLMyBatisSpringSpringBootRedisElasticSearchKafkaZookeeperDocker缓存 &#x1f4d6;知识点总结 下面是原创…

linux 下命令

linux 下命令 Linux 是一套免费使用和自 由传播的类 Unix 操作系统&#xff0c; 是一个基于 POSIX 和 UNIX 的多用户、 多任务、 支持多线程和多 CPU 的操作系统。 它能运行主要的 UNIX 工具软件、 应用程序和网络协议。 它支持 32 位和 64 位硬件。 Linux 继承了 Unix 以网络为…

uniCloud云开发----4、uniCloud云开发进阶使用方法

uniCloud云开发进阶使用方法前言1、云对象的importObject的创建和使用(1&#xff09;创建云对象&#xff08;2&#xff09;编辑云对象&#xff08;3&#xff09;在.vue文件中调用云对象&#xff08;4&#xff09;在.vue文件中调用方法2、客户端直接连接数据库(1)直接在客户端引…

设计模式-牛刀小试02

前言 本文为datawhale2022年12月组队学习《大话设计模式》最后一次打卡任务&#xff0c;本次完成homework2。 【教程地址】https://github.com/datawhalechina/sweetalk-design-pattern 一、任务描述 1.1 背景 小李已经是一个工作一年的初级工程师了&#xff0c;他所在的公…

sqlite wal 分析

动手点关注干货不迷路sqlite 提供了一种 redo log 型事务实现&#xff0c;支持读写的并发&#xff0c;见 write-ahead log&#xff08;https://sqlite.org/wal.html&#xff09;。本文将介绍 wal 原理&#xff0c;并源码剖析 checkpoint 过程&#xff0c;同时讨论下 wal 使用中…

知行之桥EDI系统如何通过ZIP端口压缩文件?

在EDI项目当中&#xff0c;对于IT技术不够成熟或设备不够完善的用户来说&#xff0c;EXCEL方案是较为适中的选择。收到合作伙伴发来的850订单之后&#xff0c;将订单数据转换为EXCEL&#xff0c;再将EXCEL发送至用户指定的邮箱。若每条订单单独发送至邮箱&#xff0c;当订单量大…

#榜样的力量#《新冠战“疫”——2022中国数据智能产业最具社会责任感企业》榜正式发布...

‍数据猿出品数据猿此次推出的#榜样的力量#《新冠战“疫”——2022中国数据智能产业最具社会责任感企业》榜单/奖项为公益主题策划活动。旨在为正能量助威&#xff0c;为中国数据智能产业中具有社会责任感的典型性企业发声。数据智能产业创新服务媒体——聚焦数智 改变商业新冠…

66、【链表】leetcode——142. 环形链表 II(C++、Python版本)

题目描述 方法一&#xff1a;哈希表 C版本 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode(int x) : val(x), next(NULL) {}* };*/ class Solution { public:ListNode *detectCycle(ListNode *head) {unord…

年终小结:苟住

大家好&#xff0c;我是一哥&#xff0c;新的一年又要到来了&#xff0c;一个词总结下今年的状态 01 工作 2022真的比较难&#xff0c;作为互联网技能从业人员&#xff0c;相信大家一定经历了很多。这一年虽然我没有被毕业&#xff0c;但也经历了公司的业务调整&#xff0c;曾…

C51 --串口通信

3.2.1 串口的连接方式 RXD&#xff1a;数据输入引脚&#xff0c;接受数据&#xff1b;STC89系列对应P3.0口&#xff0c;上官一号有单独引出 TXD&#xff1a; 数据发送引脚&#xff0c;数据发送&#xff1b;STC89系列对应P3.0口&#xff0c;上官一号有单独引出 接线方式&…