当前位置: 首页 > news >正文

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…...

为什么硬件性能监控很重要

当今的混合网络环境平衡了分布式网络和现代技术的实施。但它们并不缺少一个核心组件&#xff1a;服务器。保持网络正常运行时间归结为监控和管理导致网络停机的因素。极有可能导致性能异常的此类因素之一是硬件。使用硬件监控器监控网络硬件已成为一项关键需求。 硬件监视器是…...

HTTP缓存

HTTP缓存HTTP缓存引发的一个问题HTTP缓存的作用HTTP缓存的分类强制缓存协商缓存&#xff08;解决强缓存下资源不更新问题&#xff09;缓存策略HTTP缓存引发的一个问题 有一次在开发移动端H5项目&#xff0c;UI提了几个UI问题&#xff0c;经过样式调试&#xff0c;android上没有…...

SPI设备树处理过程

SPI设备树处理过程 文章目录SPI设备树处理过程参考资料&#xff1a;一、 spi_device结构体二、 SPI设备树格式2.1 SPI Master2.2 SPI Device2.3 设备树示例三、设备树实例3.1 使用GPIO模拟的SPI控制器3.2 IMX6ULL SPI控制器四、 设备树处理过程致谢参考资料&#xff1a; 内核头…...

数据有哪些重要的作用?

我们正处在科技高速发展的时代&#xff0c;如今互联网已经与我们的生活息息相关&#xff0c;我们每天在互联网产生大量的数据&#xff0c;这些数据散落在网络中看似没有怎么作用&#xff0c;但是这些数据经过系统的处理整合起来确实非常有价值的。 一、 发展大数据技术可以提高…...

spring面试题总结

1、spring是什么&#xff1f; spring是一个轻量级IOC和AOP容器框架&#xff0c;是为Java应用程序提供基础性服务的一套框架&#xff0c;目的是用于简化企业应用的开发&#xff0c;开发者只需要关注业务需求即可&#xff1a; core container 容器组件 spring context&#xff0c…...

使用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波

一、前言 在一些单片机或微控制器中&#xff0c;通用GPIO可以被配置为产生PWM信号。PWM即脉冲宽度调制&#xff0c;是一种用于模拟输出的技术。它可以通过改变输出信号的脉冲宽度来控制电路中的电平&#xff0c;从而实现对电路的控制。 二、什么是PWM波&#xff1f; PWM波&a…...

从产品经理的角度如何提升项目的交付质量?

提高交付质量 &#xff0c;对于每个IT公司都是永恒的话题。 交付质量其实包含2重意义&#xff0c; 一是交付的高质量&#xff08;客户角度&#xff09;&#xff0c;即客户的满意度&#xff1b;二是高质量的交付&#xff08;交付团队的角度&#xff09;&#xff0c;这里是指如何…...

JavaScript BOM【快速掌握知识点】

目录 Window对象的常用属性 语法&#xff1a; Window对象的常用方法 语法&#xff1a; open()和close()方法 History对象 常用属性和方法 示例 Location对象 常用属性 常用方法 Document对象的常用方法 定时函数 超时调用&#xff1a;setTimeout() 间歇调用&…...

【算法】哈希表

作者&#xff1a;指针不指南吗 专栏&#xff1a;算法篇 &#x1f43e;或许会很慢&#xff0c;但是不可以停下来&#x1f43e; 文章目录1.定义2.优点3.数字哈希3.1拉链法3.2开放寻址法3.3 例题4.字符串哈希1.定义 哈希表&#xff08;Hash table&#xff09;&#xff0c;是根据键…...

彻底搞懂React-hook链表构建原理

写在前面的小结 每一个 hook 函数都有对应的 hook 对象保存状态信息useContext是唯一一个不需要添加到 hook 链表的 hook 函数只有 useEffect、useLayoutEffect 以及 useImperativeHandle 这三个 hook 具有副作用&#xff0c;在 render 阶段需要给函数组件 fiber 添加对应的副…...

【数据挖掘实战】——应用系统负载分析与容量预测(ARIMA模型)

项目地址&#xff1a;Datamining_project: 数据挖掘实战项目代码 目录 一、背景和挖掘目标 1、问题背景 2、传统方法的不足 2、原始数据 3、挖掘目标 二、分析方法与过程 1、初步分析 2、总体流程 第一步&#xff1a;数据抽取 第二步&#xff1a;探索分析 第三步&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&#xff0c;隐式转换2&#xff0c;显式转换3&#xff0c;程序类转换结语前言 &#x1f33b; 大家好啊&#xff0c;我是writer桑&#xff0c;本章是关于 C# 数据类型转换的一个总结&#xff0c;其中包含隐式、显示转换和程序类转换&#xff0c;方便…...

JavaWeb JavaBean,MVC三层架构

9、JavaBean 实体类 JavaBean有特定的写法&#xff1a; 必须要有一个无参构造属性必须私有化必须有对应的get/set方法&#xff1b; 一般用来和数据库的字段做映射 ORM&#xff1b; ORM &#xff1a;对象关系映射 表—>类字段–>属性行记录---->对象 people表 …...

JavaEE简单实例——MyBatis一对多关联映射的嵌套结果集查询

简单介绍&#xff1a; 在之前的章节&#xff0c;我们简单介绍了MyBatis中的一对一的关联查询&#xff0c;使用了嵌套查询和嵌套结果集两种方式进行讲解&#xff0c;但是在实际的使用中&#xff0c;我们常用的是嵌套结果集的查询方式&#xff0c;所以在一对多的查询中&#xff…...

大数据框架之Hadoop:MapReduce(三)MapReduce框架原理——OutputFormat数据输出

3.6.1OutputFormat接口实现类 OutputFormat是MapReduce输出的基类&#xff0c;所有实现MapReduce输出都实现了OutputFormat接口。下面我们介绍几种常见的OutputFormat实现类。 1、文本输出TextOutputFormat 默认的输出格式是TextOutputFormat&#xff0c;它把每条记录写为文…...

Heritrix3与Trough集成:实现高效内容分发的完整流程

Heritrix3与Trough集成&#xff1a;实现高效内容分发的完整流程 【免费下载链接】heritrix3 Heritrix is the Internet Archives open-source, extensible, web-scale, archival-quality web crawler project. 项目地址: https://gitcode.com/gh_mirrors/he/heritrix3 …...

KittenTTS终极指南:如何在CPU上实现25MB轻量级TTS语音合成

KittenTTS终极指南&#xff1a;如何在CPU上实现25MB轻量级TTS语音合成 【免费下载链接】KittenTTS State-of-the-art TTS model under 25MB &#x1f63b; 项目地址: https://gitcode.com/gh_mirrors/ki/KittenTTS KittenTTS是一款革命性的轻量级文本转语音工具&#…...

CVAT:让计算机视觉标注效率提升80%的开源数据引擎

CVAT&#xff1a;让计算机视觉标注效率提升80%的开源数据引擎 【免费下载链接】cvat Annotate better with CVAT, the industry-leading data engine for machine learning. Used and trusted by teams at any scale, for data of any scale. 项目地址: https://gitcode.com/…...

wan2.1-vae开源模型价值:相比闭源方案节省90%图像生成API调用成本

wan2.1-vae开源模型价值&#xff1a;相比闭源方案节省90%图像生成API调用成本 你有没有算过&#xff0c;每个月花在AI图像生成上的钱有多少&#xff1f; 如果你是内容创作者、电商运营、设计师&#xff0c;或者任何需要大量图片素材的人&#xff0c;可能已经习惯了这样的场景…...

Claude Code 命令行参数实践指南

前言 很多人第一次打开 Claude Code&#xff0c;只会输入 claude&#xff0c;然后开始聊天。这当然可以&#xff0c;但就像开车只会踩油门一样——你根本没用上方向盘和变速箱。 命令行参数&#xff08;CLI Flags&#xff09;就是那些被忽视的"方向盘"。掌握它们&a…...

【花雕学编程】Arduino BLDC 之 AI 迷你小龙虾 MimiClaw 自主闭环控制机器人(带传感器反馈)

从工程视角来看&#xff0c;基于Arduino、使用互补滤波进行姿态控制的BLDC&#xff08;无刷直流电机&#xff09;机器人&#xff0c;是一个典型的嵌入式实时闭环控制系统。它集成了传感器数据融合、控制算法和电机驱动&#xff0c;广泛应用于对姿态稳定性有要求的场景。关于 Mi…...

从Tcl脚本到实战:用Innovus自动化完成数字IC后端设计的5个高效技巧

从Tcl脚本到实战&#xff1a;用Innovus自动化完成数字IC后端设计的5个高效技巧 在数字IC后端设计领域&#xff0c;效率提升往往意味着项目周期的缩短和设计质量的提高。对于已经掌握Innovus基础操作的中级工程师而言&#xff0c;如何从手动点击界面过渡到自动化脚本驱动的工作流…...

手机续航的秘密武器:深入解读LPDDR5的Power Down与Deep Sleep省电机制

手机续航的秘密武器&#xff1a;深入解读LPDDR5的Power Down与Deep Sleep省电机制 当你的手机屏幕熄灭时&#xff0c;一场精密的节能芭蕾正在内存芯片内部上演。现代智能手机中&#xff0c;LPDDR5内存的功耗可能占到整机待机功耗的30%以上&#xff0c;而Power Down与Deep Sleep…...

通义千问3-Reranker-0.6B部署教程:模型服务SLA保障(P95延迟<800ms)调优

通义千问3-Reranker-0.6B部署教程&#xff1a;模型服务SLA保障&#xff08;P95延迟<800ms&#xff09;调优 1. 为什么你需要关注这个模型&#xff1f; 如果你正在做搜索系统、智能客服或者文档问答&#xff0c;肯定遇到过这样的问题&#xff1a;用户输入一个问题&#xff…...

UG/NX Block UI Styler字符串控件避坑指南:常见问题与解决方案

UG/NX Block UI Styler字符串控件避坑指南&#xff1a;常见问题与解决方案 在UG/NX二次开发中&#xff0c;Block UI Styler作为可视化对话框设计工具&#xff0c;其字符串控件&#xff08;String Control&#xff09;是使用频率最高的交互元素之一。无论是参数输入、状态显示还…...