C++STL之list的模拟实现
目录
一.list准备
二. iterator迭代器
1._list_iterator
2.begin()、end()
3.const_begin()、const_end()
4.!=&&==
5.++ && --
6.operator*
7.operator->
三.Modify(修改)
1.insert()
2.erase()
3.push_back() && push_front()
4.pop_back&&pop_front
5.clear
四.constructor构造函数
迭代构造
拷贝构造
五.destructor析构函数
STL中list的使用也是和之前讲的类似,常用的接口就那些,可以参考list官方文档http://www.cplusplus.com/reference/list/list/?kw=list
本文章主要讲list的模拟实现.
list相当于是一个链表,对list操作相当于对链表操作
下面是对list的介绍
1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。
4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)
一.list准备
上面提到了list是双向链表,而且是由每一个结点组成,所以我们先定义结点,包括三个成员:
_data数据,_prev指向前一个结点的指针,_next指向下一个结点的指针.
当然数据类型不能确定,使用模版来解决
template<class T>class list_node{T _data;list_node<T>* _next;list_node<T>* _prev;};
结点创建完毕,就该创建链表了,链表只有一个类成员,既头结点,将上面结点名字重新定义
typedef list_node<T> Node;
private:Node* _head;
一切准备就绪,开始正式工作
构造函数中初始化时,(list的构造函数)首先new一个头结点_head,然后因为是双向循环链表,而且一开始没有数据,所以让head的prev指向head,head的next指向head.
list(){_head = new Node;_head->_next = _head;_head->_prev = _head;}
当然,还有结点的构造函数,方便我们后续进行new新的结点.
list_node(const T& x = T()):_data(x),_next(nullptr),_prev(nullptr){}
这些构造函数只是暂时需要用到的,并不完整,文章下文会补充完善的.
二. iterator迭代器
关于迭代器,上一章我说了vector的模拟实现它的内部是由原生指针实现的,它之所以可以用原生指针,主要是因为vector的空间是连续的.
而list的空间是不连续的,所以不能直接使用原生指针.
但我们可以实现一个迭代器,然后内部实现对应的各种运算符操作.例如“++”,“--”等.
然后可以直接把指针转成迭代器使用即可.
下面开始实现迭代器.
1._list_iterator
这个类就是迭代器,它看起来像一个"指针"一样,进行各种比较或各种操作,它指向的是原链表中的一个结点,所以每次必须将原链表中的一个结点传入来构造迭代器.
这样的话,它的成员类型就是结点的类型list_node.
template<class T>//(暂时)
struct _list_iterator{typedef list_node<T> Node;_list_iterator(Node* node):_node(node){}}
2.begin()、end()
这两个函数应该很常见了,返回首元素,和尾元素.
但是问题是双向链表的首尾元素分别在哪呢?
双向链表一开始存在一个头结点,这个头结点不存储任何数据,它的next才是首元素,它的prev是最后一个元素.
begin()的位置是第一个元素,end()的位置是头结点的位置,如下图
所以begin()要返回头结点的next,end()直接返回头结点即可.
iterator begin(){return iterator(_head->_next);}iterator end(){return iterator(_head);}
3.const_begin()、const_end()
之前写vector的时候,对于const类型我们直接加上const即可.但是这里就不可以了
之前我们写了begin(),返回类型是iterator,但是这个是const_iterator begin()
它俩之间不能构成重载,因为函数重载规则中没有返回值不同可以构成函数重载这一说.
这样就有了两个同名的函数,但是不能构成重载,这是编译不过去的.那我们该怎么解决呢?
可以在函数后加一个const来区分,但是当执行到_head->next时,由于加了const,_head已经不可以被解引用,引发编译错误.
这样也不可以,那该怎么办呢》
根据STL源码,它的迭代器有三个模板参数,分别为T,Ref,Ptr
普通迭代器三个参数为:T,T&,T*
const迭代器也有三个参数分别为T,const T&,const T*
所以说:STL源码是通过实例化出的对象类型不同来区分是普通对象还是const对象的.
此时代码如下:
template<class T,class Ref, class Ptr>typedef _list_iterator<T, T&, T*> iterator;typedef _list_const_iterator<T, const T&, const T*> const_iterator;const_iterator begin() const{return iterator(_head->_next);}const_iterator end() const{return iterator(_head);}
4.!=&&==
这个很简单,直接返回两个迭代器相等或不相等即可.
bool operator!=(const iterator& it){return _node != it->_node;}bool operator==(const iterator& it){return _node == it->_node;}
5.++ && --
++
++有前置++也有后置++,实现方法具体可以看我之前讲的类和对象,仔细讲解了这些思路.
前置++,既先让node指向node的next即可,然后返回*this.
iterator& operator++(){_node = _node->_next;return *this; }
后置++
为了区分前置++,需要在参数里面加一个int
思路是先保存当前结点,然后再让node指向node的next,最后返回tmp
iterator& operator++(int){iterator tmp(*this);_node = _node->_next;return tmp;}
--
前置--
和上面的思路一样,只是把next改成prev
iterator& operator--(){_node = _node->_prev;return *this;}
后置--
iterator& operator--(int){iterator tmp(*this);_node = _node->_prev;return tmp;}
6.operator*
*就是解引用,就是返回这个结点对应的值,所以直接返回data即可.
Ref此时为T&类型,可以读也可以写.
Ref operator*(){return _node->_data;}
7.operator->
->这个操作符一般用在结构体指针中,并且左操作数必须为指针类型,所以我们返回的类型也应该为指针类型,既Ptr(T*)
只不过是与*的返回类型不同
Ptr operator->(){return& (operator*()); //&(_node->data)}
三.Modify(修改)
这都是双链表的一些修改,在我之前写的里面有详细的讲解.这里便不再仔细讲述了.
1.insert()
iterator insert(iterator pos, const T& x){Node* cur = pos._node;Node* prev = cur->_prev;//创建新结点并连接Node* newnode = new Node(x);prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;return iterator(newnode);}
2.erase()
iterator 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;return iterator(next);}
3.push_back() && push_front()
这里直接复用了之前的insert代码
push_back()
void push_back(const T& x){Node* tail = _head->_prev;Node* newnode = new Node;tail->_next = newnode;newnode->_prev = tail;newnode->_next = _head;_head->_prev = newnode;}
push_front()
void push_front(const T& val){insert(begin(), val);}
4.pop_back&&pop_front
pop_back()
void pop_back(){erase(--end());}
pop_front()
void pop_front(){erase(begin());}
5.clear
思路就是从头遍历链表,直到遇到end(),每遍历一个元素,就erase一个
void clear(){iterator p = begin();while(p != end()){p = erase(p);}}
四.constructor构造函数
无参构造函数第一部分已经说了,接下来说迭代构造和拷贝构造.
由于list的初始化构造中一部分需要经常利用,我们将其封装并放入私有成员里.
private:void CreateHead(){_head = new Node;_head->_next = _head;_head->_prev = _head;}Node* _head;
迭代构造
这个类似于vector的迭代器构造,可以参考上一章
template<class InputIterator>list(InputIterator first, InputIterator last){CreatHead();while(first != last){push_back(*first);first++;}}
拷贝构造
list(const list<T>& l){CreateHead();// 用l中的元素构造临时的temp,然后与当前对象交换list<T> temp(l.cbegin(), l.cend());this->swap(temp);}
这里的swap我们需要自己实现一下,交换两个链表的头结点即可.
void swap(list<T>& lt){std::swap(_head, lt._head);}
五.destructor析构函数
这个我们可以复用之前的clear,然后最后手动释放掉头结点.
~list(){clear();delete _pHead;_pHead = nullptr;}
list的模拟实现就到此为止了.
有不懂或错误的地方欢迎提问或指正哦
相关文章:
C++STL之list的模拟实现
目录 一.list准备 二. iterator迭代器 1._list_iterator 2.begin()、end() 3.const_begin()、const_end() 4.!&& 5. && -- 6.operator* 7.operator-> 三.Modify(修改) 1.insert() 2.erase() 3.push_back() && push_front() 4.pop_bac…...
为什么硬件性能监控很重要
当今的混合网络环境平衡了分布式网络和现代技术的实施。但它们并不缺少一个核心组件:服务器。保持网络正常运行时间归结为监控和管理导致网络停机的因素。极有可能导致性能异常的此类因素之一是硬件。使用硬件监控器监控网络硬件已成为一项关键需求。 硬件监视器是…...
HTTP缓存
HTTP缓存HTTP缓存引发的一个问题HTTP缓存的作用HTTP缓存的分类强制缓存协商缓存(解决强缓存下资源不更新问题)缓存策略HTTP缓存引发的一个问题 有一次在开发移动端H5项目,UI提了几个UI问题,经过样式调试,android上没有…...
SPI设备树处理过程
SPI设备树处理过程 文章目录SPI设备树处理过程参考资料:一、 spi_device结构体二、 SPI设备树格式2.1 SPI Master2.2 SPI Device2.3 设备树示例三、设备树实例3.1 使用GPIO模拟的SPI控制器3.2 IMX6ULL SPI控制器四、 设备树处理过程致谢参考资料: 内核头…...
数据有哪些重要的作用?
我们正处在科技高速发展的时代,如今互联网已经与我们的生活息息相关,我们每天在互联网产生大量的数据,这些数据散落在网络中看似没有怎么作用,但是这些数据经过系统的处理整合起来确实非常有价值的。 一、 发展大数据技术可以提高…...
spring面试题总结
1、spring是什么? spring是一个轻量级IOC和AOP容器框架,是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用的开发,开发者只需要关注业务需求即可: core container 容器组件 spring context,…...
使用MUI与H5+构建移动端app
前言 通过mui构建APP 效果图: <!DOCTYPE html> <html> <head><meta charset...
第17篇:Java变量总结
目录 1.变量的概念 1.1 变量来源 1.2 计算机中的变量 1.3 变量如何在内存中存储 2.Java变量...
使用51单片机的GPIO输出占空比可调节的PWM波
一、前言 在一些单片机或微控制器中,通用GPIO可以被配置为产生PWM信号。PWM即脉冲宽度调制,是一种用于模拟输出的技术。它可以通过改变输出信号的脉冲宽度来控制电路中的电平,从而实现对电路的控制。 二、什么是PWM波? PWM波&a…...
从产品经理的角度如何提升项目的交付质量?
提高交付质量 ,对于每个IT公司都是永恒的话题。 交付质量其实包含2重意义, 一是交付的高质量(客户角度),即客户的满意度;二是高质量的交付(交付团队的角度),这里是指如何…...
JavaScript BOM【快速掌握知识点】
目录 Window对象的常用属性 语法: Window对象的常用方法 语法: open()和close()方法 History对象 常用属性和方法 示例 Location对象 常用属性 常用方法 Document对象的常用方法 定时函数 超时调用:setTimeout() 间歇调用&…...
【算法】哈希表
作者:指针不指南吗 专栏:算法篇 🐾或许会很慢,但是不可以停下来🐾 文章目录1.定义2.优点3.数字哈希3.1拉链法3.2开放寻址法3.3 例题4.字符串哈希1.定义 哈希表(Hash table),是根据键…...
彻底搞懂React-hook链表构建原理
写在前面的小结 每一个 hook 函数都有对应的 hook 对象保存状态信息useContext是唯一一个不需要添加到 hook 链表的 hook 函数只有 useEffect、useLayoutEffect 以及 useImperativeHandle 这三个 hook 具有副作用,在 render 阶段需要给函数组件 fiber 添加对应的副…...
【数据挖掘实战】——应用系统负载分析与容量预测(ARIMA模型)
项目地址:Datamining_project: 数据挖掘实战项目代码 目录 一、背景和挖掘目标 1、问题背景 2、传统方法的不足 2、原始数据 3、挖掘目标 二、分析方法与过程 1、初步分析 2、总体流程 第一步:数据抽取 第二步:探索分析 第三步&a…...
【华为OD机试模拟题】用 C++ 实现 - 九宫格按键输入(2023.Q1)
最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 去重求和(2023.Q1) 文章目录 最近更新的博客使用说明九宫格按键输入题目输入输出示例一输入输出说明示例二输入输出说明Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高…...
Linux: config: CONFIG_SYN_COOKIES
文章目录 CONFIG_SYN_COOKIESLinux kernel里的超时设置Huawei SBC详细工作机制CONFIG_SYN_COOKIES config SYN_COOKIES,布尔值;是否支持IP:TCP syncookie功能。 详解:一般来说TCP/IP网络不能够阻挡SYN flooding工具。这个工具很容易被利用,而且会导致DOS工具,妨碍其他整…...
【笔记】C# 数据类型转换
文章目录前言类型转换的概念1,隐式转换2,显式转换3,程序类转换结语前言 🌻 大家好啊,我是writer桑,本章是关于 C# 数据类型转换的一个总结,其中包含隐式、显示转换和程序类转换,方便…...
JavaWeb JavaBean,MVC三层架构
9、JavaBean 实体类 JavaBean有特定的写法: 必须要有一个无参构造属性必须私有化必须有对应的get/set方法; 一般用来和数据库的字段做映射 ORM; ORM :对象关系映射 表—>类字段–>属性行记录---->对象 people表 …...
JavaEE简单实例——MyBatis一对多关联映射的嵌套结果集查询
简单介绍: 在之前的章节,我们简单介绍了MyBatis中的一对一的关联查询,使用了嵌套查询和嵌套结果集两种方式进行讲解,但是在实际的使用中,我们常用的是嵌套结果集的查询方式,所以在一对多的查询中ÿ…...
大数据框架之Hadoop:MapReduce(三)MapReduce框架原理——OutputFormat数据输出
3.6.1OutputFormat接口实现类 OutputFormat是MapReduce输出的基类,所有实现MapReduce输出都实现了OutputFormat接口。下面我们介绍几种常见的OutputFormat实现类。 1、文本输出TextOutputFormat 默认的输出格式是TextOutputFormat,它把每条记录写为文…...
Linux搜索、编辑
目录 1.搜索 1.1.基础用法 1.2.高级用法 2.编辑 2.1.vim简洁 2.2.vim快捷键 1.搜索 1.1.基础用法 find命令用于搜索,格式如下: find 指定目录 -匹配方式 所要匹配的关键字 所要匹配的关键字支持通配符,?代表一个字符*代表任意个字符。 如果想设…...
Git Commit提交规范总结
文章目录前言git commit 提交规范提交消息头(commit message header)提交消息具体内容(commit message body)提交消息尾述(commit message footer)Revert表情(Emojis)标识idea插件其他操作Commitizen生成 Change logGit获取提交消息格式化输出相关参考前言 我们都知道…...
【ESP 保姆级教程】疯狂毕设篇 —— 案例:基于ESP8266和EMQX的教室灯光控制系统
忘记过去,超越自己 ❤️ 博客主页 单片机菜鸟哥,一个野生非专业硬件IOT爱好者 ❤️❤️ 本篇创建记录 2023-02-26 ❤️❤️ 本篇更新记录 2022-02-26 ❤️🎉 欢迎关注 🔎点赞 👍收藏 ⭐️留言📝🙏 此博客均由博主单独编写,不存在任何商业团队运营,如发现错误,请…...
SpringBoot (一) 项目构建、配置读取、静态资源定义
哈喽,大家好,我是有勇气的牛排(全网同名)🐮 有问题的小伙伴欢迎在文末评论,点赞、收藏是对我最大的支持!!!。 前言 SpringBoot是基于Spring开发的开源项目,…...
<JVM上篇:内存与垃圾回收篇>12 - 垃圾回收相关概念
笔记来源:尚硅谷 JVM 全套教程,百万播放,全网巅峰(宋红康详解 java 虚拟机) 文章目录12.1. System.gc()的理解12.2. 内存溢出与内存泄露内存溢出(OOM)内存泄漏(Memory Leakÿ…...
new操作符做了什么?
new是什么? new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。 function Person (name,age) {this.name namethis.age age } Person.prototype.sayName function () {console.log(this.name) } let man new Person(xl,20) consol…...
Java_IO流,书城IO版
1.字符IO流的输入/输出 首先,IO流根据多方面划分。 根据方向划分 输入流/输出流根据处理单元划分 字节流/字符流根据功能划分 节点流/处理流 尝试一下使用字符输入流在读写文件: package IOStream;import java.io.*;public class Test {public stati…...
2023自动化测试岗位需求的 7 项必备技能 (最新版)
目录:导读 一、自动化测试员技能——编程语言 二、自动化测试员技能–出色的手动测试技能 三、.自动化测试员技能–自动化工具专业知识 四、自动化测试员技能–了解业务需求 五、自动化测试员技能–自动化工具故障排除 六、自动化测试员技能–具有测试管理工具…...
【华为OD机试模拟题】用 C++ 实现 - 路灯照明(2023.Q1)
最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 去重求和(2023.Q1) 文章目录 最近更新的博客使用说明路灯照明【华为OD机试模拟题】题目输入输出描述示例一输入输出说明Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高…...
学到贫血之-贫血模型和充血模型
学习自:设计模式之美 1 基于贫血模型的传统开发模式 // ControllerVO(View Object) public class UserController {private UserService userService; //通过构造函数或者IOC框架注入public UserVo getUserById(Long userId) {UserBo userBo userService.getUser…...
做网站天通苑/网络营销策划书
数据插值方法x测试结果2.拟合方法Polyfit Function[p,s]polyfit(x,y,n);返回多项式系数向量p和矩阵s,s与polyval函数一起用时,可以得到预测值的误差估计。[p,s,mu]polyfit(x,y,n);返回多项式的系数,利用该函数进行多项式曲线拟合评价。Polyva…...
wordpress 评论go跳转/百度官方电话号码
最近跑老外的程序[1] 又出问题了:??? Error: File: computeScores.m Line: 17 Column: 28Unbalanced or unexpected parenthesis or bracket.我用的是Matlab R2009a,识别出了程序里的几处语法错误:img gray2rgb(img); %always have 3 cha…...
企业网站推广的方法有( )/宁波网站seo哪家好
无论你是初学者还是经验丰富的开发人员,对于你和你的团队来说,提高异常处理的能力可以更好的解决问题。Java中的异常处理并不是一件容易的事,初学者会觉得很难理解,即使是经验丰富的开发人员也可能需要花费几个小时来讨论应该如何…...
室内设计工作室网站怎么做/腾讯会议价格
文章目录前言一、MySQL入门基础第二天学习二、开始学习列类型(字段类型)无符号标识设定显示长度小数类型浮点型FloatDouble定点数Decimal时间日期类型Mysql记录长度字符串型TextEnumSet列属性Null属性列描述主键随表创建表后增加查看主键删除主键复合主键…...
单位建网站的详细步骤/推广策略包括哪些内容
(注意:遇到程序在弄懂之后一定要自己去敲,一定要自己去敲,一定要自己去敲) (注意:遇到程序在弄懂之后一定要自己去敲,一定要自己去敲,一定要自己去敲) (注意:遇到程序在弄懂之后一定要自己去敲&…...
共享互助医疗网站建设/西安楼市最新房价
什么是mock接口呢,举个栗子,你在一家电商公司,有查看商品、购物、支付、发 货、收获等等等一大堆功能,你是一个测试人员,测测测,测到支付功能的时候,你就要调用第三方支付接口了,真实…...