C++_list
目录
一、模拟实现list
1、list的基本结构
2、迭代器封装
2.1 正向迭代器
2.2 反向迭代器
3、指定位置插入
4、指定位置删除
5、结语
前言:
list是STL(标准模板库)中的八大容器之一,而STL属于C++标准库的一部分,因此在C++中可以直接使用list容器存储数据。list实质上是一个带头双向循环链表,这也使得他能够在常数的时间复杂度范围内插入和删除数据,缺点是不能像数组那样进行元素下标的随机访问。
一、模拟实现list
在C++中可以直接调用库里的list,并且使用起来非常的简便,和使用vector、string相差无几,但是为了能够更好的了解list和其底层原理,下文会对lsit的常用接口进行模拟实现,以便对list有更深入的理解,并且list的底层实现逻辑完美的表现了面向对象的思想。
1、list的基本结构
与实现vector和string不一样,list可以分成两个整体:链表本身、节点本身,因此需要两个类(链表类、节点类)完成list的基本实现,并且把节点类看作是一个普通的节点,而实现链表的具体功能函数都放在链表类中。
list基本功能代码如下:
#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;namespace ZH
{template <class T>struct list_node//节点类{list_node<T>* prev;list_node<T>* next;T val;list_node(const T& t):prev(nullptr), next(nullptr), val(t){}};template <class T>class list//链表类{public:typedef list_node<T> node;//让节点类看起来像一个普通的节点list()//构造函数、目的是创建哨兵位头节点:_node(new node(-1)){_node->next = _node;_node->prev = _node;}void push_front(const T& t)//头插{node* newnode = new node(t);node* cur = _node->next;_node->next = newnode;newnode->prev = _node;newnode->next = cur;cur->prev = newnode;}void Print()//打印数据{node* cur = _node->next;while (cur != _node){cout << cur->val << " ";cur = cur->next;}}~list()//析构{node* cur = _node->next;node* dest = cur->next;while (cur != _node){delete cur;cur = dest;dest = dest->next;}delete _node;_node = nullptr;}private:node* _node;};
}int main()
{ZH::list<int> lt;lt.push_front(1);lt.push_front(2);lt.push_front(3);lt.push_front(4);lt.Print();return 0;
}
运行结果:
从上面的代码可以发现, list的底层实现和之前c语言中实现双向循环链表的逻辑一模一样,只不过用C++的方式将其进行了封装。
这里节点类里的成员变量要放在公有域(struct定义类默认为公有域),因为在list中会改变节点前后指针的指向,因此节点的指针要设为外部可见。
2、迭代器封装
2.1 正向迭代器
在调用库里面的list,会发现库里面list的迭代器用起来像是一个指针,因为可以对迭代器进行解引用操作以及++操作。所以在模拟实现迭代器时,我们也用一个指针来模拟迭代器的行为,不同的是指针进行++操作时,会指向该地址的下一个地址,而我们期望的迭代器++可以指向下一个节点。
示意图如下:
因此,如果单单的把指针看成迭代器则无法实现遍历链表的功能,所以要实现迭代器必须对指针进行又一层的封装,也就是把迭代器写成一个类,但是该类的底层还是一个节点指针,只是对该指针的操作有了新的规定。
正向迭代器代码实现如下:
#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;namespace ZH
{template <class T>struct list_node//节点类{list_node<T>* prev;list_node<T>* next;T val;list_node(const T& t):prev(nullptr), next(nullptr), val(t){}};template<class T>struct list_iterator//迭代器类{typedef list_node<T> node;typedef list_iterator<T> self;node* _node;// 成员变量list_iterator(node* node):_node(node){}bool operator!=(const self& s)//重载!={return _node != s._node;}self& operator++()//重载前置++{_node = _node->next;return *this;}T& operator*()//重载解引用{return _node->val;}};template <class T>class list//链表类{public:typedef list_node<T> node;//让节点类看起来像一个普通的节点typedef list_iterator<T> iterator;//让迭代器类看起来像一个迭代器list()//构造函数、目的是创建哨兵位头节点:_node(new node(-1)){_node->next = _node;_node->prev = _node;}void push_front(const T& t)//头插{node* newnode = new node(t);node* cur = _node->next;_node->next = newnode;newnode->prev = _node;newnode->next = cur;cur->prev = newnode;}iterator begin(){//begin返回的是一个指向头节点的下一个节点的迭代器对象return iterator(_node->next);//匿名对象创建并返回}iterator end(){//begin返回的是一个指向头节点的迭代器对象return iterator(_node);//匿名对象创建并返回}~list()//析构{node* cur = _node->next;node* dest = cur->next;while (cur != _node){delete cur;cur = dest;dest = dest->next;}delete _node;_node = nullptr;}private:node* _node;//指向哨兵位的头节点};
}int main()
{ZH::list<int> lt;lt.push_front(1);lt.push_front(2);lt.push_front(3);lt.push_front(4);ZH::list<int>::iterator it = lt.begin();while (it!=lt.end()){cout << *it << " ";++it;}return 0;
}
运行结果:
注意,这里的it要看成是一个对象,他的类型是 list<int>::iterator。ZH::list<int>::iterator it = lt.begin();这句代码实际上是调用了拷贝构造,用begin()的返回对象构造了一个新的对象it。
2.2 反向迭代器
反向迭代器与正向迭代器的区别在于,反向迭代器是从链表的最后一个节点开始往前遍历的,并且他的逻辑和正向迭代器的逻辑是相反的,可以确定的一点是反向迭代器也是通过一个类来实现的。
反向迭代器实现代码如下:
#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;namespace ZH
{template <class T>struct list_node//节点类{list_node<T>* prev;list_node<T>* next;T val;list_node(const T& t):prev(nullptr), next(nullptr), val(t){}};template<class T>struct list_iterator//迭代器类{typedef list_node<T> node;typedef list_iterator<T> self;node* _node;// 成员变量list_iterator(node* node):_node(node){}bool operator!=(const self& s)//重载!={return _node != s._node;}self& operator++()//重载前置++{_node = _node->next;return *this;}self& operator--()//重载前置--{_node = _node->prev;return *this;}T& operator*()//重载解引用{return _node->val;}};template<class T>class list_reverse_iterator{public:typedef list_iterator<T> iterator;typedef list_reverse_iterator<T> rself;list_reverse_iterator(iterator it):rit(it){}bool operator!=(const rself& s)//重载!={return rit!= s.rit;//复用正向迭代器的!=重载}rself& operator++()//重载前置++{--rit;//复用正向迭代器的++return *this;}T& operator*()//重载解引用{return *rit;//复用正向迭代器的解引用}private:iterator rit;};template <class T>class list//链表类{public:typedef list_node<T> node;//让节点类看起来像一个普通的节点typedef list_iterator<T> iterator;//让迭代器类看起来像一个迭代器typedef list_reverse_iterator<T> reverse_iterator;list()//构造函数、目的是创建哨兵位头节点:_node(new node(-1)){_node->next = _node;_node->prev = _node;}void push_front(const T& t)//头插{node* newnode = new node(t);node* cur = _node->next;_node->next = newnode;newnode->prev = _node;newnode->next = cur;cur->prev = newnode;}reverse_iterator rbegin(){return reverse_iterator(iterator(_node->prev));//返回最后一个节点}reverse_iterator rend(){return reverse_iterator(iterator(_node));//返回头节点}~list()//析构{node* cur = _node->next;node* dest = cur->next;while (cur != _node){delete cur;cur = dest;dest = dest->next;}delete _node;_node = nullptr;}private:node* _node;//指向哨兵位的头节点};
}int main()
{ZH::list<int> lt;lt.push_front(1);lt.push_front(2);lt.push_front(3);lt.push_front(4);ZH::list<int>::reverse_iterator rit = lt.rbegin();while (rit != lt.rend()){cout << *rit << " ";++rit;}return 0;
}
运行结果:
从上述代码中可以发现,反向迭代器是复用正向迭代器的成员函数达到实现的,只不过在反向迭代器类里进行又一层包装。
3、指定位置插入
有了迭代器,就可以实现从指定位置插入了,指定位置插入代码如下:
void Insert(iterator pos, const T& val)//插入{node* newnode = new node(val);node* cur = pos._node;node* prev = cur->prev;prev->next = newnode;newnode->prev = prev;newnode->next = cur;cur->prev = newnode;}
值得注意的是,list的插入不会导致迭代器失效,因为即使在该迭代器指向节点的前面插入一个数据,则该迭代器还是指向该节点的。
4、指定位置删除
指定位置删除代码如下:
void Erase(iterator pos)//删除{assert(pos != end());node* cur = pos._node;node* prev = cur->prev;node* next = cur->next;prev->next = next;next->prev = prev;delete cur;}
删除逻辑示意图:
删除与插入不同在于,删除之后迭代器会失效,因为此时it指向的是一块被回收的区域,不能直接访问it所指向的区域,会导致野指针问题。
5、结语
以上就是关于list如何实现的讲解,list的模拟实现完全体现了面向对象的思想,链表本身、节点以及迭代器都被封装成一个类,从用户的角度看,是在对象的层面上直接进行操作的,但是底层却是各种复用,依旧是通过操作内置类型来实现上层的对象之间的操作。
最后希望本文可以给你带来更多的收获,如果本文对你起到了帮助,希望可以动动小指头帮忙点赞👍+关注😎+收藏👌!如果有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!!
相关文章:

C++_list
目录 一、模拟实现list 1、list的基本结构 2、迭代器封装 2.1 正向迭代器 2.2 反向迭代器 3、指定位置插入 4、指定位置删除 5、结语 前言: list是STL(标准模板库)中的八大容器之一,而STL属于C标准库的一部分,因此在C中可以直接使用…...
使用docker部署mongodb
1.创建目录 mkdir -p /opt/mongodb/{data,logs,config} 2.创建配置文件 进入目录 cd /opt写入配置 vim mongod.conf 内容如下 systemLog:# MongoDB发送所有日志输出的目标指定为文件destination: file# mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径path:…...

C#,打印漂亮的贝尔三角形(Bell Triangle)的源程序
以贝尔数为基础,参考杨辉三角形,也可以生成贝尔三角形(Bell triangle),也称为艾特肯阵列(Aitkens Array),皮埃斯三角形(Peirce Triangle)。 贝尔三角形的构造…...
开源电商系统
前言 做电商永不过时,但形式会不断变化。任何赚钱的事情大体都分为两大块:生产和销售。两者是并重的,首先要有好的产品,其次是做好推广运营和销售渠道建设。对于小微企业来说,前期如果能通过销售赚到第一桶金…...

责任链模式在java中的实现
1 总览 2 概念 避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。职责链模式是一种对象行为型模式。 3 实现 公共部分,一个系…...

粤嵌Gec6818---小项目功能实现简单步骤(RFID+图片显示+音乐+视频)
项目设计开发环境: (1)VMware Workstation Pro软件 (2)ubuntu12 .04 (能交叉编译就行) (3)SecureCRT (4)代码编译器(notepad/Vis…...
opencv学习 特征提取
内容来源于《opencv4应用开发入门、进阶与工程化实践》 图像金字塔 略 拉普拉斯金字塔 对输入图像进行reduce操作会生成不同分辨率的图像,对这些图像进行expand操作,然后使用reduce减去expand之后的结果,就会得到拉普拉斯金字塔图像。 …...

关于maven项目构建的解释
在Idea中使用模块化构建项目 项目介绍: sky-take-out sky-common pom.xml sky-pojo pom.xml sky-server pom.xml pom.xml 说明 sky-server依赖sky-pojo和sky-common,继承sky-take-outsky-pojo继承sky-take-outsky-common继承sky-take-out 由于Idea编…...

IMU/捷联惯导常见的术语,以及性能评价标准(附Python解析代码)
0. 简介 现在的机器人领域在普遍使用IMU(惯性导航单元)。该系统有三个加速度传感器与三个角速度传感器(陀螺)组成,加速度计用来感受飞机相对于地垂线的加速度分量,陀螺仪用来感知飞机的角速率变化…...
Debezium发布历史98
原文地址: https://debezium.io/blog/2020/11/12/debezium-1-3-1-final-released/ 欢迎关注留言,我是收集整理小能手,工具翻译,仅供参考,笔芯笔芯. Debezium 1.3.1.Final 发布 十一月 12, 2020 作者: 克里…...

APUE学习之进程间通信(IPC)(下篇)
目录 一、进程间通信(IPC) 二、信号量(Semaphore) 1、基本概念 2、同步关系与互斥关系 3、临界区与临界资源 4、信号量的工作原理 5、信号量编程 6、实战演练 三、共享内存(Shared Memory) 1、…...
【Java 设计模式】行为型之中介者模式
文章目录 1. 定义2. 应用场景3. 代码实现结语 中介者模式(Mediator Pattern)是一种行为型设计模式,用于通过一个中介对象来集中管理多个对象之间的交互关系,从而降低对象之间的耦合度。中介者模式通过将对象之间的通信委托给中介者…...

MySql 慢SQL配置,查询,处理
一.慢SQL配置相关 1.查看慢SQL是否开启 执行下面命令查看是否开启慢SQL show variables like %slow_query_log; 复制代码 OFF: 未开启ON: 2.打开慢SQL配置 执行下面的命令开启慢查询日志 set global slow_query_logON; 复制代码 3.修改慢查询阈值 前面介绍了SQL执行到达了…...

算法:分界线
一、算法描述 电视剧《分界线》里面有一个片段,男主为了向警察透露案件细节,且不暴露自己,于是将报刊上的字 剪切下来,剪拼成匿名信。 现在有一名举报人,希望借鉴这种手段,使用英文报刊完成举报操作。 但为…...

STM32单片机基本原理与应用(四)
直流电机驱动控制原理 1、电机正反转控制 在STM32中,直流电机的正反转控制主要通过改变电机输入电源的极性来实现。当电机的电压极性发生变化时,电机的旋转方向也会相应改变。在硬件电路中,可以通过继电器或晶体管等电子开关来切换电机的电源…...

elk之安装和简单配置
写在前面 本文看下elk的安装和简单配置,安装我们会尝试通过不同的方式来完成,也会介绍如何使用docker,docker-compose安装。 1:安装es 1.1:安装单实例 下载es安装包 在这里 下载,下载后解压到某个目录…...
springboot(ssm环保网站 绿色环保宣传系统Java系统
springboot(ssm环保网站 绿色环保宣传系统Java系统 开发语言:Java 框架:springboot(可改ssm) vue JDK版本:JDK1.8(或11) 服务器:tomcat 数据库:mysql 5.7࿰…...

【MBtiles数据索引和服务发布】GeoServer改造Springboot番外系列二
xyz地图服务访问示例:http://192.168.1.240:8081/gmserver/raster/xyz/firstWP:Imagery-raster/{z}/{x}/{y}.jpg 访问示例如下: mbtiles目录结构 根据z,x,y获取对应mbtiles文件路径的工具方法 说明:重点是使用getMb…...
Redis抓取数据到Logstash再推到Elasticsearch集群
一、安装Logstash 前面安装过Logstash了,不做解释直接跳过 参考:上一篇文章 二、配置Logstash 在logstash目录下,编辑我们之前的配置文件logstash.conf vim logstash.confinput、output字面意思,从redis去拿取数据,输出到Elasticsearch data_type:数据类型为list k…...

【代码随想录-链表】反转链表
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老导航 檀越剑指大厂系列:全面总结 jav…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?
论文网址:pdf 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记,谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...

Golang——6、指针和结构体
指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...
【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权
摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题:安全。文章将详细阐述认证(Authentication) 与授权(Authorization的核心概念,对比传统 Session-Cookie 与现代 JWT(JS…...

算术操作符与类型转换:从基础到精通
目录 前言:从基础到实践——探索运算符与类型转换的奥秘 算术操作符超级详解 算术操作符:、-、*、/、% 赋值操作符:和复合赋值 单⽬操作符:、--、、- 前言:从基础到实践——探索运算符与类型转换的奥秘 在先前的文…...

基于江科大stm32屏幕驱动,实现OLED多级菜单(动画效果),结构体链表实现(独创源码)
引言 在嵌入式系统中,用户界面的设计往往直接影响到用户体验。本文将以STM32微控制器和OLED显示屏为例,介绍如何实现一个多级菜单系统。该系统支持用户通过按键导航菜单,执行相应操作,并提供平滑的滚动动画效果。 本文设计了一个…...
Java多线程实现之Runnable接口深度解析
Java多线程实现之Runnable接口深度解析 一、Runnable接口概述1.1 接口定义1.2 与Thread类的关系1.3 使用Runnable接口的优势 二、Runnable接口的基本实现方式2.1 传统方式实现Runnable接口2.2 使用匿名内部类实现Runnable接口2.3 使用Lambda表达式实现Runnable接口 三、Runnabl…...