【STL】:list的模拟实现

朋友们、伙计们,我们又见面了,本期来给大家解读一下有关list的模拟实现,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成!

C 语 言 专 栏:C语言:从入门到精通

数据结构专栏:数据结构

个  人  主  页 :stackY、

C + + 专 栏   :C++

Linux 专 栏  :Linux

目录

1. 基本构造

2. 正向迭代器

2.1 非const迭代器

2.2 const迭代器

2.3 正向迭代器的改进优化

3. 修改相关接口

3.1 insert、erase

3.2 尾插、头插、尾删、头删、清理

4. 拷贝构造、赋值重载、析构

5. 完整代码


1. 基本构造

list的底层其实是一个带头双向循环的链表,所以在模拟实现之前可以看一下库里面怎么实现的:

namespace ywh
{//链表结构template<class T>struct list_node{T _data;                 //节点中的数据list_node<T>* _prev;    //指向前一个节点的指针list_node<T>* _next;    //指向后一个节点的指针//构造list_node(const T& x = T()):_data(x), _prev(nullptr), _next(nullptr){}};//list结构template<class T>class list{public:typedef list_node<T> Node;public://空初始化void empty_init(){_head = new Node;_head->_prev = _head;_head->_next = _next;}//构造list(){empty_init();}private:Node* _head;  //链表的头节点size_t _size; //节点个数};
}

2. 正向迭代器

在使用list的阶段的迭代器使用方法和之前的string、vector一样,但是了解过链表的结构都知道,链表要访问下一个节点就是使用节点中的next指针指向下一个节点,需要访问里面的数据需要找到里面的data,++和解引用并不能访问链表的节点,那么迭代器的使用在底层肯定是封装加运算符重载。

在实现迭代器的时候也需要采用封装和运算符重载,运算符重载需要用到++、--、!=、==、*、->等运算符。迭代器分为const迭代器和非const迭代器,首先先来看看非const迭代器:

2.1 非const迭代器

迭代器的实现一般使用struct来进行封装:

namespace ljm
{//链表结构template<class T>struct list_node{T _data;                 //节点中的数据list_node<T>* _prev;    //指向前一个节点的指针list_node<T>* _next;    //指向后一个节点的指针//构造list_node(const T& x = T()):_data(x), _prev(nullptr), _next(nullptr){}};//非const正向迭代器template<class T>struct __list_iterator{typedef list_node<T> Node;typedef __list_iterator<T> self;Node* _node;//迭代器构造__list_iterator(Node* node):_node(node){}//前置//operator++self& operator++(){return _node->_next;}//operator--self& operator--(){return _node->_prev;}//后置self operator++(int){self* tmp(_node);_node = _node->_next;return tmp;}//operator--self operator--(int){self* tmp(_node);_node = _node->_prev;return tmp;}//operator*T& operator*(){return _node->_data;}//operator->T* operator->(){return &_node->_data;}//operator!=bool operator!=(const self& s){return _node != s._node;}//operator==bool operator==(const self& s){return _node == s._node;}};//list结构template<class T>class list{public:typedef list_node<T> Node;public://空初始化void empty_init(){_head = new Node;_head->_prev = _head;_head->_next = _head;}//构造list(){empty_init();}///正向迭代器iterator begin(){return iterator(_head->_next); //使用匿名对象进行构造}iterator end(){return iterator(_head);}private:Node* _head;  //链表的头节点size_t _size; //节点个数};
}

2.2 const迭代器

迭代器中涉及到修改的就是*和->,const迭代器的作用是指向的内容不能修改,本身不能修改,所以需要重新进行封装:

namespace ljm
{//链表结构template<class T>struct list_node{T _data;                 //节点中的数据list_node<T>* _prev;    //指向前一个节点的指针list_node<T>* _next;    //指向后一个节点的指针//构造list_node(const T& x = T()):_data(x), _prev(nullptr), _next(nullptr){}};//非const正向迭代器//...//const正向迭代器template<class T>struct __list_const_iterator{typedef list_node<T> Node;typedef __list_const_iterator<T> self;Node* _node;//迭代器构造__list_const_iterator(Node* node):_node(node){}//前置//operator++self& operator++(){_node = _node->_next;return *this;}//operator--self& operator--(){_node = _node->_prev;return *this;}//后置self operator++(int){self* tmp(_node);_node = _node->_next;return tmp;}//operator--self operator--(int){self* tmp(_node);_node = _node->_prev;return tmp;}//operator*const T& operator*(){return _node->_data;}//operator->const T* operator->(){return &_node->_data;}//operator!=bool operator!=(const self& s){return _node != s._node;}//operator==bool operator==(const self& s){return _node == s._node;}};//list结构template<class T>class list{public:typedef list_node<T> Node;typedef __list_iterator<T> iterator;typedef __list_const_iterator<T> const_iterator;public:基本构造/////空初始化void empty_init(){_head = new Node;_head->_prev = _head;_head->_next = _head;}//构造list(){empty_init();}///正向迭代器iterator begin(){return iterator(_head->_next); //使用匿名对象进行构造}iterator end(){return iterator(_head);}const_iterator begin() const{return const_iterator(_head->_next);}const_iterator end() const{return const_iterator(_head);}private:Node* _head;  //链表的头节点size_t _size; //节点个数};
}

2.3 正向迭代器的改进优化

上面的这两种写法不难看出有很多代码都是重复出现的,使得代码非常冗余,那么怎么将这些冗余的代码进行合并呢?

我们可以看一下库里面如何实现:

采用泛型编程,使用模板,将这一转化的工作交给编译器,而我们只需传递const参数和非const参数即可:

namespace ywh
{//链表结构template<class T>struct list_node{T _data;                 //节点中的数据list_node<T>* _prev;    //指向前一个节点的指针list_node<T>* _next;    //指向后一个节点的指针//构造list_node(const T& x = T()):_data(x), _prev(nullptr), _next(nullptr){}};//非const正向迭代器//   类型模板参数   传递引用      传递指针template<class T, class Ref, class Ptr>struct __list_iterator{typedef list_node<T> Node;typedef __list_iterator<T, Ref, Ptr> self;Node* _node;//迭代器构造__list_iterator(Node* node):_node(node){}//前置//operator++self& operator++(){_node = _node->_next;return *this;}//operator--self& operator--(){_node = _node->_prev;return *this;}//后置self operator++(int){self* tmp(_node);_node = _node->_next;return tmp;}//operator--self operator--(int){self* tmp(_node);_node = _node->_prev;return tmp;}//operator*Ref operator*(){return _node->_data;}//operator->Ptr operator->(){return &_node->_data;}//operator!=bool operator!=(const self& s){return _node != s._node;}//operator==bool operator==(const self& s){return _node == s._node;}};//list结构template<class T>class list{public:typedef list_node<T> Node;typedef __list_iterator<T, T&, T*> iterator;   //非const迭代器typedef __list_iterator<T, const T&, const T*> const_iterator;  //const迭代器public:基本构造/////空初始化void empty_init(){_head = new Node;_head->_prev = _head;_head->_next = _head;}//构造list(){empty_init();}///正向迭代器iterator begin(){return iterator(_head->_next); //使用匿名对象进行构造}iterator end(){return iterator(_head);}const_iterator begin() const{return const_iterator(_head->_next);}const_iterator end() const{return const_iterator(_head);}private:Node* _head;  //链表的头节点size_t _size; //节点个数};
}

3. 修改相关接口

3.1 insert、erase

在pos位置进行插入删除比较简单,需要进行链接新节点,改变节点中指针的指向即可,库里面对于插入和删除都带有返回值,那么我们在实现的时候也加上返回值:

///修改相关接口//在pos位置插入节点iterator insert(iterator pos, const T& x){//创建新新节点Node* newnode = new Node(x);//链接节点Node* cur = pos._node;Node* prev = cur->_prev;prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;//更新节点个数++_size;//返回新节点的位置return iterator(newnode);}//删掉pos位置的节点iterator erase(iterator pos){//保存相对位置Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;//链接节点delete cur;prev->_next = next;next->_prev = prev;//更新节点个数--_size;//返回pos的下一个位置return iterator(next);}

在进行insert之后可以看到迭代器是不会失效的,但是在erase之后pos位置会被释放,因此erase之后迭代器会失效,在使用前先更新迭代器。

3.2 尾插、头插、尾删、头删、清理

当实现了insert和erase之后,实现这些接口直接复用即可:

        //尾插void push_back(const T& x){insert(end(), x);}//头插void push_front(const T& x){insert(begin(), x);}//尾删void pop_back(){erase(--end());}//头删void pop_front(){erase(begin());}//清理void clear(){iterator it = begin();while (it != end()){it = erase(it);}}

4. 拷贝构造、赋值重载、析构

在这里我们都是采用现代写法:

 拷贝构造直接使用迭代器依次遍历进行尾插。

//拷贝构造list(const list<T>& lt){//先创建空节点empty_init();//依次尾插即可for (auto e : lt){push_back(e);}}//operator=void swap(list<T>& lt){std::swap(_head, lt._head);std::swap(_size, lt._size);}list<T>& operator=(list<T> lt){swap(lt);return *this;}//析构~list(){clear();delete _head;_head = nullptr;_size = 0;}

5. 完整代码

 头文件:List.h

#pragma oncenamespace ywh
{//链表结构template<class T>struct list_node{T _data;                 //节点中的数据list_node<T>* _prev;    //指向前一个节点的指针list_node<T>* _next;    //指向后一个节点的指针//构造list_node(const T& x = T()):_data(x), _prev(nullptr), _next(nullptr){}};//非const正向迭代器//   类型模板参数   传递引用      传递指针template<class T, class Ref, class Ptr>struct __list_iterator{typedef list_node<T> Node;typedef __list_iterator<T, Ref, Ptr> self;Node* _node;//迭代器构造__list_iterator(Node* node):_node(node){}//前置//operator++self& operator++(){_node = _node->_next;return *this;}//operator--self& operator--(){_node = _node->_prev;return *this;}//后置self operator++(int){self* tmp(_node);_node = _node->_next;return tmp;}//operator--self operator--(int){self* tmp(_node);_node = _node->_prev;return tmp;}//operator*Ref operator*(){return _node->_data;}//operator->Ptr operator->(){return &_node->_data;}//operator!=bool operator!=(const self& s){return _node != s._node;}//operator==bool operator==(const self& s){return _node == s._node;}};//list结构template<class T>class list{public:typedef list_node<T> Node;typedef __list_iterator<T, T&, T*> iterator;   //非const迭代器typedef __list_iterator<T, const T&, const T*> const_iterator;  //const迭代器public:基本构造/////空初始化void empty_init(){_head = new Node;_head->_prev = _head;_head->_next = _head;}//构造list(){empty_init();}//拷贝构造list(const list<T>& lt){//先创建空节点empty_init();//依次尾插即可for (auto e : lt){push_back(e);}}//operator=void swap(list<T>& lt){std::swap(_head, lt._head);std::swap(_size, lt._size);}list<T>& operator=(list<T> lt){swap(lt);return *this;}//析构~list(){clear();delete _head;_head = nullptr;_size = 0;}///正向迭代器iterator begin(){return iterator(_head->_next); //使用匿名对象进行构造}iterator end(){return iterator(_head);}const_iterator begin() const{return const_iterator(_head->_next);}const_iterator end() const{return const_iterator(_head);}///修改相关接口//在pos位置插入节点iterator insert(iterator pos, const T& x){//创建新新节点Node* newnode = new Node(x);//链接节点Node* cur = pos._node;Node* prev = cur->_prev;prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;//更新节点个数++_size;//返回新节点的位置return iterator(newnode);}//删掉pos位置的节点iterator erase(iterator pos){//保存相对位置Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;//链接节点delete cur;prev->_next = next;next->_prev = prev;//更新节点个数--_size;//返回pos的下一个位置return iterator(next);}//尾插void push_back(const T& x){insert(end(), x);}//头插void push_front(const T& x){insert(begin(), x);}//尾删void pop_back(){erase(--end());}//头删void pop_front(){erase(begin());}//清理void clear(){iterator it = begin();while (it != end()){it = erase(it);}}//节点个数size_t size(){return _size;}private:Node* _head;  //链表的头节点size_t _size; //节点个数};
}

朋友们、伙计们,美好的时光总是短暂的,我们本期的的分享就到此结束,欲知后事如何,请听下回分解~,最后看完别忘了留下你们弥足珍贵的三连喔,感谢大家的支持!  

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】编译安装nginx,手写service配置文件,深度理解systemd控制管理服务底层原理

目录 一、了解服务 1、服务的本质 2、centos7的systemd的服务 3、service unit file配置文件的组成以及掌握常用选项 4、关于systemd管理的命令学习 5、运行级别 二、编译安装nginx&#xff0c;以及手写service配置文件&#xff0c;请看注释 ​编辑 一、了解服务 1、服…

【C语言】函数的系统化精讲(二)

&#x1f308;write in front :&#x1f50d;个人主页 &#xff1a; 啊森要自信的主页 &#x1f308;作者寄语 &#x1f308;&#xff1a; 小菜鸟的力量不在于它的体型&#xff0c;而在于它内心的勇气和无限的潜能&#xff0c;只要你有决心&#xff0c;就没有什么事情是不可能的…

AI系统ChatGPT程序源码+AI绘画系统源码+支持GPT4.0+Midjourney绘画

一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如…

【计算机网络】数据链路层-MAC和ARP协议

文章目录 1. 认识以太网2. MAC协议MAC帧的格式MAC地址和IP地址的区别MTU 3. 局域网通信原理碰撞检测和避免 4. ARP协议ARP数据报的格式ARP缓存 1. 认识以太网 网络层解决的是跨网络点到点传输的问题&#xff0c;数据链路层解决的是同一网络中的通信。 数据链路层负责在同一局域…

SpringBoot + Vue2项目打包部署到服务器后,使用Nginx配置SSL证书,配置访问HTTP协议转HTTPS协议

配置nginx.conf文件&#xff0c;这个文件一般在/etc/nginx/...中&#xff0c;由于每个人的体质不一样&#xff0c;也有可能在别的路径里&#xff0c;自己找找... # 配置工作进程的最大连接数 events {worker_connections 1024; }# 配置HTTP服务 http {# 导入mime.types配置文件…

聚观早报 |小鹏P7i 550版上市;零一万物发布大模型

【聚观365】11月7日消息 小鹏P7i 550版上市 零一万物发布大模型 vivo X100现身Geekbench 小马智行与丰田联合发布Robotaxi 王云鹏出任百度IDG负责人 小鹏P7i 550版上市 小鹏P7i 550版正式上市&#xff0c;新车共推出550 Pro、550 Max 两款新版型&#xff0c;售价分别为22…

WPF中的Binding的常见知识点与技巧

完全来源于十月的寒流&#xff0c;感谢大佬讲解 在XAML中&#xff0c;可以绑定到许多不同类型的数据源和属性。以下是一些可以绑定的常见数据源和属性&#xff1a; 属性&#xff1a;可以绑定到对象的属性&#xff0c;例如控件的Text、Visibility、IsEnabled等属性。 集合&am…

民宿酒店服务预约小程序的作用

民宿往往是旅游者们前往某个城市感受风情常住的地方&#xff0c;也因此在景区或特定地方&#xff0c;总是不乏大小民宿品牌&#xff0c;但除了市场高需求外&#xff0c;商家们所遇的痛点也不少&#xff1a; 1、获客引流难 民宿生意虽然需求量高&#xff0c;但各家品牌众多&am…

微服务架构——笔记(3)Eureka

微服务架构——笔记&#xff08;3&#xff09; 基于分布式的微服务架构 本次笔记为 此次项目的记录&#xff0c;便于整理思路&#xff0c;仅供参考&#xff0c;笔者也将会让程序更加完善 内容包括&#xff1a;1.支付模块、2.消费者订单模块、支付微服务入驻Eureka、Eureka集群…

Docker 多阶段构建的原理及构建过程展示

Docker多阶段构建是一个优秀的技术&#xff0c;可以显著减少 Docker 镜像的大小&#xff0c;从而加快镜像的构建速度&#xff0c;并减少镜像的传输时间和存储空间。本文将详细介绍 Docker 多阶段构建的原理、用途以及示例。 Docker 多阶段构建的原理 在传统的 Docker 镜像构建…

Hive 解析 JSON 字符串数据的实现方式

文章目录 通过方法解析现实示例 通过序列化实现示例 通过方法解析现实 在 Hive 中提供了直接解析 JSON 字符串数据的方法 get_json_object(json_txt, path)&#xff0c;该方法参数解析如下&#xff1a; json_txt&#xff1a;顾名思义&#xff0c;就是 JSON 字符串&#xff1b;…

vue3项目搭建

一&#xff1a;介绍 Vue3 是从2018年开始研发&#xff0c;经过大概一年半的不断重构与试运行&#xff0c;最终发布于2020年9月18日。相比于 Vue2 其具有更小&#xff0c;更快&#xff0c;支持性更高等功能。因此学号 Vue3 是非常有必要的&#xff0c;同时 Vue2 也将会与2023年1…

JAVA前端开发介绍

以一个网站为例包括网站设计、前端开发、程序开发等。网站设计就是网站的外观&#xff0c;平面的东西。程序开发也好理解就是功能实现。而前端开发&#xff0c;简单来说&#xff0c;就是把平面效果图转换成网页&#xff0c;把静态转换成动态。它的工作包括了:切图、写样式、做鼠…

Java根据一个List内Object的两个字段去重

背景 在Java开发过程中&#xff0c;我们经常会遇到需要对List进行去重的需求。 其中常见的情况是&#xff0c;将数组去重&#xff0c;或者将对象依据某个字段去重。这两种方式均可用set属性进行处理。 今天讨论&#xff0c;有一个List&#xff0c;且其中的元素是自定义的对象&…

【JVM】JDBC案例打破双亲委派机制

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaEE 操作系统 Redis 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 JVM 打破双亲委派机制&#xff08;JDBC案例…

RK3568平台开发系列讲解(音视频篇)RTMP 推流

🚀返回专栏总目录 文章目录 一、RTMP 的工作原理二、RTMP 流媒体服务框架2.1、Nginx 流媒体服务器2.2、FFmpeg 推流沉淀、分享、成长,让自己和他人都能有所收获!😄 📢目前常见的视频监控和视频直播都是使用了 RTMP、RTSP、HLS、MPEG-DASH、 WebRTC流媒体传输协议等。 R…

Halcon WPF 开发学习笔记(1):Hello World小程序

文章目录 文章专栏视频链接Hello World训练图片训练目的 开始训练图像预处理导入图像三通道处理调用算子通道选取 滤波什么是好的滤波 增加对比度 区域选取阈值处理算子参数选择运行结果(红色为选择区域) 区域分割运行结果 特征筛选参数代码第二次&#xff0c;面积筛选 画选中十…

【每日一题】统计范围内的元音字符串数

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;遍历 其他语言python3 写在最后 Tag 【遍历】【数组】【2023-11-07】 题目来源 2586. 统计范围内的元音字符串数 题目解读 统计范围内的元音字符串数。 解题思路 方法一&#xff1a;遍历 遍历下标在 [left, right]…

APM建设踩了哪些坑?去哪儿旅行分布式链路追踪系统实践

一分钟精华速览 分布式链路追踪系统在企业的APM体系中扮演着重要的角色。本文分享了去哪儿旅行构建分布式链路追踪系统的实践经验。从APM整体架构设计入手&#xff0c;讲述了日志收集、Kafka传输和Flink任务处理等环节的性能优化实践和踩坑经验。 同时&#xff0c;作者结合丰…

JVM-垃圾回收

目录 1、GC过程 2、垃圾回收算法 2.1、标记-清除 2.2、标记-整理 2.3、复制 2.4、分代收集算法 3、TLAB 4、对象如何进入老年代 5、卡片标记 6、HotSpot垃圾回收器 6.1、年轻代垃圾回收器 6.2、老年代垃圾回收器 6.3、如何配置垃圾回收器 6.4、STW 7、CMS垃圾回收…