【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; //节点个数}; }
朋友们、伙计们,美好的时光总是短暂的,我们本期的的分享就到此结束,欲知后事如何,请听下回分解~,最后看完别忘了留下你们弥足珍贵的三连喔,感谢大家的支持!
相关文章:
【STL】:list的模拟实现
朋友们、伙计们,我们又见面了,本期来给大家解读一下有关list的模拟实现,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成! C 语 言 专 栏:C语言:从入门到精通 数据…...
第七章 图【数据结构与算法】【精致版】
第七章 图【数据结构与算法】【精致版】 前言版权第七章 图7.1 应用实例7.2图的基本概念7.3图的存储结构7.3.1邻接矩阵**1-邻接矩阵.c****2-邻接矩阵plus.c** 7.3.2 邻接表**3-邻接表.c** **4-邻接表plus.c** 7.3.3 十字链表7.3.4多重链表 7.4图的遍历7.4.1深度优先搜索遍历**5…...
模型蒸馏学习
知识蒸馏:获取学生网络和教师网络指定蒸馏位点的输出特征并计算蒸馏 loss 的过程 知乎-mmrazor-模型蒸馏 知识蒸馏算法往往分为 reponse-based基于响应、feature-based基于特征 和 relation-based基于关系三类。 也可为 data-free KD、online KD、self KDÿ…...
总结Kibana DevTools如何操作elasticsearch的常用语句
一、操作es的工具 ElasticSearch HeadKibana DevToolsElasticHQ 本文主要是总结Kibana DevTools操作es的语句。 二、搜索文档 1、根据ID查询单个记录 GET /course_idx/_doc/course:202、term 匹配"name"字段的值为"6789999"的文档 类似于sql语句中的等…...
【QT】QT自定义C++类
在使用Qt的ui设计时,Qt为我们提供了标准的类,但是在很多复杂工程中,标准的类并不能满足所有的需求,这时就需要我们自定义C类。 下面以自定义的QPushButton作一个很简单的例子。 先新建默认Qt Widgets Application项目 一、自定义…...
【多媒体文件格式】AVI、WAV、RIFF
AVI、RIFF AVI:Audio/Video Interleaved(音频视频交织/交错),用于采集、编辑、播放的RIFF文件。由Microsoft公司1992年11月推出,是Microsoft公司开发的一种符合RIFF文件规范的数字音频与视频文件格式,原先…...
AI创作系统ChatGPT商业运营系统源码+支持GPT4/支持ai绘画
一、AI创作系统 SparkAi创作系统是基于OpenAI很火的ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统,支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美,可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如…...
JWT简介 JWT结构 JWT示例 前端添加JWT令牌功能 后端程序
目录 1. JWT简述 1.1 什么是JWT 1.2 为什么使用JWT 1.3 JWT结构 1.4 验证过程 2. JWT示例 2.1 后台程序 2.2 前台加入jwt令牌功能 1. JWT简述 1.1 什么是JWT Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7…...
Rust核心功能之一(所有权)
目录 1、什么是所有权? 1.1 所有权规则 1.2 变量作用域 1.3 String 类型 1.4 内存与分配 变量与数据交互的方式(一):移动 变量与数据交互的方式(二):克隆 只在栈上的数据:拷贝…...
跨域(CORS)和JWT 详解
跨域 (CORS) 概念 同源策略 (Same-Origin Policy) 同源策略是一项浏览器安全特性,它限制了一个网页中的脚本如何与另一个来源(域名、协议、端口)的资源进行交互。这对于防止跨站点请求伪造和数据泄露非常重要。 为什么需要跨域? 跨域问题通…...
前端框架Vue学习 ——(二)Vue常用指令
文章目录 常用指令 常用指令 指令: HTML 标签上带有 “v-” 前缀的特殊属性,不同指令具有不同含义。例如: v-if, v-for… 常用指令: v-bind:为 HTML 标签绑定属性值,如设置 href,css 样式等 <a v-bind:href"…...
Linux 指令心法(十四)`flash_erase` 擦除Flash存储器
文章目录 flash_erase 作用flash_erase的主要特点和使用场景flash_erase命令应用方法flash_erase命令可以解决哪些问题?flash_erase命令使用时注意事项 flash_erase 作用 这是一个用于擦除Flash存储器的命令。它可以擦除指定的Flash块或扇区,以便在写入新数据之前…...
GoLong的学习之路(二十一)进阶,语法之并发(go最重要的特点)(协程的主要用法)
并发编程在当前软件领域是一个非常重要的概念,随着CPU等硬件的发展,我们无一例外的想让我们的程序运行的快一点、再快一点。Go语言在语言层面天生支持并发,充分利用现代CPU的多核优势,这也是Go语言能够大范围流行的一个很重要的原…...
加快网站收录 3小时百度收录新站方法
加快网站收录 3小时百度收录新站方法 3小时百度收录新站方法说起来大家可能不相信,但这确实是真实的(该方法是通过技术提交,让百度快速抓取收录您的网站,不管你网站有没有备案,都能在短时间内被收录,要是你的网站迟迟不…...
GPT实战系列-ChatGLM3本地部署CUDA11+1080Ti+显卡24G实战方案
目录 一、ChatGLM3 模型 二、资源需求 三、部署安装 配置环境 安装过程 低成本配置部署方案 四、启动 ChatGLM3 五、功能测试 新鲜出炉,国产 GPT 版本迭代更新啦~清华团队刚刚发布ChatGLM3,恰逢云栖大会前百川也发布Baichuan2-192K,一…...
图片怎么转换成pdf?
图片怎么转换成pdf?图片可以转换成PDF格式文档吗?当然是可以的呀,当图片转换成PDF文件类型时,我们就会发现图片更加方便的打开分享和传播,而且还可以更加安全的保证我们的图片所有性。我们知道PDF文档是可以加密的&…...
【源码】医学影像PACS实现三维影像后处理等功能
医学影像诊断技术近年来取得了快速发展,包括高性能的影像检查设备的临床应用和数字信息技术的图像显示、存储、传输、处理、识别,这些技术使得计算机辅助检测和诊断成为可能,同时人工智能影像诊断也进入了人们的视野。这些技术进步提高了疾病…...
DOCTYPE是什么,有何作用、 使用方式、渲染模式、严格模式和怪异模式的区别?
前言 持续学习总结输出中,今天分享的是DOCTYPE是什么,有何作用、 使用方式、渲染模式、严格模式和怪异模式的区别。 DOCTYPE是什么,有何作用? DOCTYPE是HTML5的文档声明,通过它可以告诉浏览器,使用那个H…...
Go语言实现HTTP正向代理
文章目录 前言实现思路代码实现 前言 正向代理(Forward Proxy)是一种代理服务器的部署方式,它位于客户端和目标服务器之间,代表客户端向目标服务器发送请求。正向代理可以用来隐藏客户端的真实身份,以及在不同网络环境…...
第11章_数据处理之增删改
文章目录 1 插入数据1.1 实际问题1.2 方式 1:VALUES的方式添加1.3 方式2:将查询结果插入到表中演示代码 2 更新数据演示代码 3 删除数据演示代码 4 MySQL8新特性:计算列演示代码 5 综合案例课后练习 1 插入数据 1.1 实际问题 解决方式&#…...
极致精简,功能强大的PDF编辑工具
这是一款功能全面的PDF编辑工具 你只需要导入一份PDF格式文件 就可以快速的对它进行插入 批注编辑保护转换等各种操作 而且无需登录 也可以直接使用 在插入选项中可以进行插入文字图片 页面页眉页脚页码文档背景水印视频音频等 在批注选项中可以管理批注隐藏批注 高亮显示 文本…...
2026 新视角:化妆品开发的底层逻辑,做好一款产品,从选对原料开始
在化妆品研发链条中,配方架构、生产工艺、包装设计固然重要,但决定一款产品上限的,永远是原料。一款稳定、安全、表现优异的护肤成品,离不开纯净、达标、批次一致的优质原料。对于品牌方、配方师、代工企业而言,原料不…...
Lindy自动化效率翻倍的秘密:从零搭建高可靠多步骤任务流的7步黄金流程
更多请点击: https://intelliparadigm.com 第一章:Lindy自动化效率翻倍的秘密:从零搭建高可靠多步骤任务流的7步黄金流程 Lindy自动化平台以“越久越可靠”为设计哲学,将经典软件工程原则与现代可观测性实践深度融合。其核心优势…...
Transient、QuickEye、VerifyEye傻傻分不清?一文讲透Ansys里三种眼图仿真方法的适用场景与避坑指南
Transient、QuickEye、VerifyEye深度解析:Ansys眼图仿真技术选型实战指南 在高速数字系统设计中,眼图分析是评估信号完整性的黄金标准。面对Ansys工具链中三种截然不同的眼图生成方法,工程师常常陷入选择困境——是追求精确度的传统瞬态分析&…...
独立站内容分层:一层给 SEO,一层给 GEO
你的内容在喂两个完全不同的"阅读者" 你的博客文章,从来都不只有一个读者。 传统认知里,独立站内容的读者只有两类:真人访客和搜索引擎爬虫。SEO 优化的一切工作,本质上都是在讨好后者,顺带服务前者。 但…...
MongoDB Limit 与 Skip 方法详解
MongoDB Limit 与 Skip 方法详解 引言 MongoDB 是一个高性能、可伸缩的文档存储系统,它提供了强大的数据存储和查询功能。在处理大量数据时,Limit 与 Skip 方法是 MongoDB 中常用的查询优化工具。本文将详细介绍 MongoDB 中的 Limit 与 Skip 方法,包括其基本用法、性能影响…...
Web渗透测试能力成长地图:从工具使用到漏洞认知跃迁
1. 这不是工具清单,而是一张Web渗透测试的“能力成长地图”你刚点开这篇文章,大概率正站在两个路口之间:一边是网上铺天盖地的“十大免费扫描器推荐”,点进去全是截图下载链接一句“一键扫漏洞”,结果装完跑两下&#…...
谷氨酸发酵过程的软测量建模【附模型】
✨ 长期致力于软测量、谷氨酸发酵、动力学模型、支持向量机、高斯过程、变量选择、异常状态研究工作,擅长数据搜集与处理、建模仿真、程序编写、仿真设计。 ✅ 专业定制毕设、代码 ✅ 如需沟通交流,点击《获取方式》 (1)多阶段高斯…...
PCB的常规机械通孔与HDI工艺钻孔差异
结合常规 4 层通孔 PCB(非 HDI) 标准制程,分步骤讲清钻孔时机、先后顺序,区分机械通孔与板件结构,专业且贴合工厂实际流程。一、先明确 4 层通孔板基础结构4 层板结构:L1 → PP 半固化片 → L2/L3ÿ…...
Java项目中如何提升整体系统性能?
性能优化可以说是我们程序员的必修课,如果你想要跳出CRUD的苦海,成为一个更“高级”的程序员的话,性能优化这一关你是无论无何都要去面对的。为了提升系统性能,开发人员可以从系统的各个角度和层次对系统进行优化。除了最常见的代…...


