list模拟

之前模拟了string,vector,再到现在的list,list的迭代器封装最让我影响深刻。本次模拟的list是双向带头节点的循环链表,该结构虽然看起来比较复杂,但是却非常有利于我们做删除节点的操作,结构图如下。 

 由于其节点结构特点,使得头插头删,尾插尾删的时间复杂度为O(1),因为我们只需要改变两边节点的指针链接即可。

节点类

    先来个开胃小菜,了解了解节点类,该类比较简单。由于每个节点既要存储数据,又要存储前后节点指针,所以我们封装一个结构体,用的struct而不是class, 因为我们在后面的函数需要访问节点存储的节点指针成员来达到遍历的效果,所以用的是struct。至于封装性,别人也不敢直接访问我节点存的指针变量,举个例子,如果他拿到了我的节点指针ptr,访问成员用ptr->+(成员名字),问题就在成员名字,每个人实现的类名函数名那都是五花八门,这就导致这段访问成员的代码就不具有平台移值性。

template <class T>  struct list_node{typedef list_node<T> node; 这里记忆一下node是节点类型名的重命名,后面还有很多重命名,免得搞混了。list_node(const T& val=T())T()是一个匿名对象,当我们未传对象初始化时,可用一个匿名对象来初始化value:_next(nullptr), _prev(nullptr)   用初始化列表来初始化,因为_val存的若是自定义类型, _val(val)        想要调用非默认构造函数得用初始化列表。 {;}node* _next;node* _prev;T _val;};

链表正向迭代器类:

  不知道还记不记得string和vector的迭代器,string的iterator就是char*的重命名,而vector则是T*的重命名,那为什么list的迭代器不能把node*直接typedef呢,然后begin()函数返回哨兵位的下一个节点地址,end()函数返回哨兵位节点地址。假设我们就这样实现list的迭代器,然后结合下面的使用场景。

my_list::list<int>::iterator it = l1.begin();
while (it != l1.end())
{it++;
}

   大家有没有想过it++到哪去了呢?是下一个节点吗?当然不是啦,链表的每个节点都是孤立的,你++怎么能到下一个节点呢?当时我立马就想到运算符重载了,在重载函数内部++指针是往后走,还是往前走,又或者是++到指针变成下一个节点地址那不都是我们实现者说了算。冷静,冷静,运算符重载首要原则就是操作数应该是自定义类型,所有类型的指针都是内置类型,所以节点指针它就没办法重载运算符,所以我们要把节点指针封装成类,这样我们才可以对这个类进行运算符重载,使得it++调用类内重载函数,到下一个节点。

template<class T,class Ref,class Ptr> Ref和Ptr这两个模板参数是为了实现const迭代器struct _list_iterator   使其成员函数可在类外访问{typedef list_node<T> node;typedef _list_iterator<T,Ref,Ptr> Self;_list_iterator(node* pnode=nullptr)//用一个node指针初始化其成员:_node(pnode){;}//无析构函数,迭代器不能释放节点_list_iterator(const Self& l)//临时对象的引用,例如接受begin():_node(l._node){;}Self& operator++()//this指针是个迭代器类型的指针{_node = _node->_next;return (*this);}Self operator++(int)首先这两个++运算符重载还构成函数重载编译默认给前置++多传一个参数,使其调用这个函数,如果你把这两个函数参数换一下,前置++去掉int,后置++加上int你就会发现前置++会去调用后置++的函数,因为编译器还是用参数匹配或者函数名修饰规则来找函数。{Self tmp = (*this);_node = _node->_next;return (tmp);}Self& operator--(){_node = _node->_prev;return *this;}Self operator--(int){Self tmp = (*this);_node = _node->_prev;return tmp;}

bool operator!=(const Self& l)//要加const,因为外部可能是与end()比较{return (_node != l._node);}bool operator==(const Self& l){return (_node == l._node);//判断迭代器内部的node指针是否相等}因为普通迭代器就一两个成员函数的返回值不同,比较简单的方法是用模板参数Ref控制返回const T&还是返回T&,Ref operator*(){return (_node->_val);//返回节点数据}//用类封装节点指针,使其可以重载++,*等运算符node* _node;};

我们实现的const迭代器并不是直接对迭代器类加个const,而是对特定的函数返回值做const修饰,所以迭代器成员函数无需加const.

 ->重载针对的是节点数据为自定义类型A,A类型存着一个变量_val,的时候,比如有一个类实例化对象为it, 我们希望重载->,使得下面的能调用到自定义类型内的变量,it->_val,但是it->返回的只是节点存的整个结构体A的地址,我们应该用it->->_val才访问到_val,而且这个重载也只能返回
地址,如果返回的是一个结构体对象,我们就要it->.(这有个点操作符)_val才能访问,更加奇怪,

但是当我们去测试的时候发现我们仅仅用it->_val即可访问,实际上是编译器会帮我们多加一个->,为了保证重载运算符的可读性,只能这样改了。还有如果节点存的数据成员是内置类型,我想应该是不可以用->重载的,你想返回的是int*, 但->必须是指向类的。

		Ptr operator->()const第三模板参数原因,控制返回const T*或T*,数据不可修改{return &operator*();  }

反向迭代器类

  针对list链表,我们可以直接定义一个反向迭代器类叫reverse_iterator,反向迭代器中的成员函数只有++,--和正向迭代器不一样,所以看完正向迭代器的成员函数也就能理解反向迭代器的成员 *解引用重载都是返回节点数据,->重载都是调用operator*()重载再取地址。

namespace my_list
{template<class iterator, class Ref, class Ptr>class reverse_iterator{public:typedef reverse_iterator<iterator, Ref, Ptr> Self;reverse_iterator(iterator it):_it(it)显示调用正向迭代器的拷贝构造{;}reverse_iterator(const Self& l):_it(l._it){;}Self& operator++()  复用正向迭代器_it的--函数{_it--;return (*this);}Self operator++(int){Self tmp = (*this);_it--;return (tmp);}Self& operator--()   复用正向迭代器_it的++函数{_it++;return *this;}Self operator--(int){Self tmp = (*this);_it++;return tmp;}bool operator!=(const Self& l){return (_it != l._it);  此处调用的是正向迭代器内部的比较}bool operator==(const Self& l){return (_it == l._it);}Ref operator*(){iterator tmp = _it;return *(--tmp);}Ptr operator->(){return &operator*();}private:iterator _it;};
}

链表类

 我们知道目前设计的链表节点会保存前后位置的指针,这意味着链表类只需要管理头节点指针即可管理所有链表节点,有哨兵位则保存哨兵位地址,没有则保存第一个节点的地址。

template<class T>class list{typedef list_node<T> node;//设为私有,不让外部通过该处访问public:typedef _list_iterator<T, T&, T*> iterator;//设为公有typedef _list_iterator<T, const T&, const T*> const_iterator;调用const迭代器就传const T&和const T*控制函数返回值不被修改就达到const迭代器作用了下面这两个迭代器的typedef顺序不能改,
不然可能会将reverse_iterator<const_iterator, const T&, const T*>中的reverse_iterator识别为已经typedef的reverse_iterator<iterator, T&, T*>typedef reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;typedef reverse_iterator<iterator, T&, T*> reverse_iterator;reverse_iterator rbegin(){return ((reverse_iterator)end());}const_reverse_iterator rbegin() const{return (const_reverse_iterator)end();}reverse_iterator rend(){return (reverse_iterator)begin();}const_reverse_iterator rend() const{return (const_reverse_iterator)begin();}iterator begin(){return _phead->_next;//返回匿名对象}iterator end(){return _phead;}const_iterator begin()const{return _phead->_next;//返回匿名对象}const_iterator end()const   要加const,要让const list<T>调用该函数,返回const迭代器{return _phead;}void Createhead(){_phead = new node;_phead->_next = _phead;_phead->_prev = _phead;}list(){Createhead();}list(const list<T>& l)//list的拷贝构造{Createhead();for (auto& c : l){push_back(c);}}list(size_t n, const T& value = T()){Createhead();while (n--){push_back(value);}}list(int n, const T& value = T()){Createhead();while (n--){push_back(value);}}template<class inputiterator>list(inputiterator first, inputiterator last) 用一段迭代器区间初始化{Createhead();while (first != last){push_back(*first);++first;}}void swap(list<int>& lt){std::swap(_phead, lt._phead);}list<T>& operator=(list<int> lt)//list<int>可用list代替{swap(lt);return *this;}~list(){clear();delete _phead;_phead = nullptr;}void clear(){iterator it = begin();while (it != end()){it = erase(it);}}void push_front(const T& value = T()){insert(begin(), value);}void push_back(const T& value = T()){//node* newnode = new node(value);//node* tail = _phead->_prev;链接新节点,注意链接关系//tail->_next = newnode;//newnode->_prev = tail;//newnode->_next = _phead;//_phead->_prev = newnode;insert(end(), value);}iterator insert(iterator pos, const T& value = T()){node* newnode = new node(value);node* pose = pos._node;node* cur = pose->_prev;cur->_next = newnode;newnode->_prev = cur;newnode->_next = pose;pose->_prev = newnode;return newnode;}void pop_back(){erase(--end());}void pop_front(){erase(begin());}size_t size()const{int n = 0;iterator it = begin();while (it != end()){it++;n++;}return n;}bool empty()const{return size() == 0;}iterator erase(iterator pos){node* pose = pos._node;//迭代器it是一个类,访问成员变量用.操作符pos._node = pos._node->_next;node* cur = pose->_prev;cur->_next = pose->_next;pose->_next->_prev = cur;delete pose;return pos;}private:node* _phead;};
}

上述我们封装了四个类,虽然一下子不知道如何说清楚为什么要分开描述,如果都挤在一起,那肯定是有点冗余,类本质是用来描述的,个人认为一种类应该是只描述一种对象的,如果一个类既描述链表节点,又描述链表,我感觉对于我后续的实现会很麻烦。

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

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

相关文章

【etcd】docker 启动单点 etcd

etcd: v3.5.9 etcd-browser: rustyx/etcdv3-browser:latest 本文档主要描述用 docker 部署单点的 etcd&#xff0c; 用 etcd-browser 来查看注册到 etcd 的 key 默认配置启动 docker run -d --name ai-etcd --networkhost --restart always \-v $PWD/etcd.conf.yml:/opt/bitn…

从gRPC入门到放弃

文章目录 gRPCgRPC是什么为什么要用gRPC安装gRPC安装gRPC安装Protocol Buffers v3安装插件检查 gRPC的开发方式编写.proto文件定义服务生成指定语言的代码编写业务逻辑代码 gRPC入门示例编写proto代码编写Server端Go代码编写Client端Go代码gRPC跨语言调用生成Python代码编写Pyt…

K8s安全配置:CIS基准与kube-bench工具

01、概述 K8s集群往往会因为配置不当导致存在入侵风险&#xff0c;如K8S组件的未授权访问、容器逃逸和横向攻击等。为了保护K8s集群的安全&#xff0c;我们必须仔细检查安全配置。 CIS Kubernetes基准提供了集群安全配置的最佳实践&#xff0c;主要聚焦在两个方面&#xff1a;主…

基于双层优化的微电网系统规划设计方法(Matlab代码实现)

目录 &#x1f4a5;1 概述 1.1 微电网系统结构 1.2 微电网系统双层规划设计结构 1.3 双层优化模型 1.4 上层容量优化模型 1.5 下层调度优化模型 &#x1f4da;2 运行结果 &#x1f389;3 文献来源 &#x1f308;4 Matlab代码、数据、文章讲解 &#x1f4a5;1 概述 文献来源&…

牛客网Verilog刷题——VL51

牛客网Verilog刷题——VL51 题目答案 题目 请编写一个十六进制计数器模块&#xff0c;计数器输出信号递增每次到达0&#xff0c;给出指示信号zero&#xff0c;当置位信号set 有效时&#xff0c;将当前输出置为输入的数值set_num。模块的接口信号图如下&#xff1a; 模块的时序图…

作者推荐 | 【底层服务/编程功底系列】「底层技术原理」史上最清晰的采用程序员的视角方式进行深入探索Linux零拷贝技术原理及实现

采用程序员的视角方式进行深入探索Linux零拷贝技术原理及实现 背景介绍什么是零拷贝第一步&#xff1a;用户空间数据复制到内核空间第二步&#xff1a;用户空间数据复制到内核空间第三步&#xff1a;用户空间数据再次复制到内核空间第四步&#xff1a;内核态数据buffer写回到So…

html5播放器视频切换和连续播放的实例

当前播放器实例可以使用changeVid接口切换正在播放的视频。当有多个视频&#xff0c;在上一个视频播放完毕时&#xff0c;自动播放下一个视频时也可采用该处理方式。 const option {vid: 88083abbf5bcf1356e05d39666be527a_8,//autoplay: true,//playsafe: , //PC端播放加密视…

超详细|ChatGPT论文润色教程

本文讲述使用中科大开源ChatGPT论文辅助工具&#xff0c;对论文进行润色 祝看到本教程的小伙伴们都完成论文&#xff0c;顺利毕业。 可以加QQ群交流&#xff0c;一群&#xff1a; 123589938 第一章 介绍 今天给大家分享一款非常不错的ChatGPT论文辅助工具&#xff0c;使用了专…

电脑更新win10黑屏解决方法

电脑更新win10黑屏解决方法 电脑黑屏出现原因解决步骤 彻底解决 电脑黑屏 出现原因 系统未更新成功就关机&#xff0c;导致系统出故障无法关机 解决步骤 首先长安电源键10s关机 按电源键开机&#xff0c;出现logo时按F8进入安全模式。 进入自动修复环境后&#xff0c;单击…

ElasticSearch 7.x

前言 elastic表示可伸缩&#xff0c;search表示查询。所以es的核心即为查询。通常情况下&#xff0c;我们的数据可以分为三类&#xff1a;结构化数据、非结构化数据、半结构化数据。 结构化数据&#xff1a;一般会用特定的结构来组织和管理数据&#xff0c;表现为二维表结构。…

Spring Bean的生命周期

文章目录 Spring Bean的生命周期加载Bean对象创建Bean对象构造对象填充属性初始化实例注册销毁 销毁 Spring Bean的生命周期 Spring Bean的生命周期就是指Bean对象从创建到销毁的过程&#xff0c;大体可以分为&#xff1a;实例化、属性赋值、初始化、使用、销毁。 加载Bean对象…

【数据分析】numpy (二)

numpy作为数据分析&#xff0c;深度学习常用的库&#xff0c;本篇博客我们来介绍numpy的一些进阶用法&#xff1a; 一&#xff0c;numpy的常用简单内置函数&#xff1a; 1.1求和&#xff1a; a np.array([[1, 2],[3, 4]]) np.sum(a)10 1.2求平均值&#xff1a; np.mean(a…

向 Maven 中央仓库上传一个修改过的基于jeecg的autoPOI的 jar包记录

1、注册https://issues.sonatype.org/账号 下面就代表注册好了&#xff0c;同时提交的工单也通过了 2、这里主要是goupId 需要进行认证&#xff0c;需要到域名注册商近一个txt的解析&#xff0c;以便确保这个是你的 通过下面来验证你的域名信息&#xff0c;这里主要是上面的工…

学生信息管理系统springboot学校学籍专业数据java jsp源代码mysql

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 学生信息管理系统springboot 系统3权限&#xff1a;超…

如何使用fiddler进行抓包

首先需要下载fiddler&#xff0c;推荐使用bing搜索引擎搜索&#xff08;百度搜狗一般搜这种工具展示的前几个全都是广告&#xff09;&#xff0c;直接搜索fiddler&#xff0c;搜出来第一个fiddler官网 然后直接点击download下载 进入下载页面后&#xff0c;正确填写一个邮箱&a…

unity TextMeshPro 富文本

<b>粗体标签</b> <i>斜体标签</i> <u>下划线标签</u> <s>删除线标签</s> <sup>上标标签</sup>前面后边上标签 5<sup>。</sup>C <sub>下标标签&#xff0c;如&#xff1a;</sub>H<sub&…

【雕爷学编程】MicroPython动手做(33)——物联网之天气预报2

天气&#xff08;自然现象&#xff09; 是指某一个地区距离地表较近的大气层在短时间内的具体状态。而天气现象则是指发生在大气中的各种自然现象&#xff0c;即某瞬时内大气中各种气象要素&#xff08;如气温、气压、湿度、风、云、雾、雨、闪、雪、霜、雷、雹、霾等&#xff…

庄懂的TA笔记(十九)<特效:顶点 平移+缩放+旋转+幽灵夜巡效果)>

庄懂的TA笔记&#xff08;十九&#xff09;&#xff1c;特效&#xff1a;顶点 平移缩放旋转幽灵夜巡效果)&#xff1e; 大纲&#xff1a; 效果展示&#xff1a; 正文&#xff1a; 一、顶点平移&#xff1a; 1、代码实现&#xff1a; 1.1、声明移动范围&#xff0c;移动速度。 _…

灵魂在高纬度的19个特征

01 喜欢奉献和付出‍ 在高维度的星球里&#xff0c;无私的爱是主旋律&#xff0c;所以是每个灵魂自动的反应。 02 喜欢光明&#xff0c;不喜欢黑暗 喜欢展现自己的阳光的一面&#xff0c;不喜欢自己黑暗的一面、尽量克制自己的不好的思维和行为&#xff0c;所以容易成为一个完…

微信小程序测试策略和注意事项?

一、测试前准备&#xff08;环境搭建&#xff09; 1、前端页面 微信 Web 开发者工具安装、授权测试用的微信号可预览和调试小程序 2、管理后台 配置内网测试服务器环境&#xff0c;通过 PC 端 Web 站点管理小程序前端的输出内容&#xff0c;可从开发人员获取管理账号进行测…