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.mzph.cn/news/17905.shtml

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

相关文章

【《C# 10 和 .NET 6入门与跨平台开发(第6版)》——一本循序渐进的C#指南】

这个新版本对上一版做了全面修订&#xff0c;涵盖C# 10和.NET 6的所有新功能. 本书讨论面向对象编程、编写函数、测试函数、调试函数、实现接口以及继承类等主题&#xff1b;介绍.NET API&#xff0c;这些API可执行多种任务&#xff0c;如管理和查询数据&#xff0c;监视和改进…

解决mysqld服务启动失败

原因如下&#xff1a; 1、进程占用 首先查看下mysql进程: ps -aux | grep mysql有进程号占用了&#xff0c;kill 这个进程号 再重启服务 2、所有者和所属组为mysql 查看/usr/local/MySQL/data/mysqld.pid所有者和所属组是否为mysql 原来是权限有问题&#xff0c…

保留网络:大型语言模型的Transformer继任者

原文信息 原文题目&#xff1a;《Retentive Network: A Successor to Transformer for Large Language Models》 原文引用&#xff1a;Sun Y, Dong L, Huang S, et al. Retentive Network: A Successor to Transformer for Large Language Models[J]. arXiv preprint arXiv:2…

论文阅读 - Few-shot Network Anomaly Detection via Cross-network Meta-learning

论文链接&#xff1a;https://arxiv.org/pdf/2102.11165.pdf 目录 摘要&#xff1a; 引言 问题定义 方法 Graph Deviation Networks Cross-network Meta-learning 摘要&#xff1a; 网络异常检测旨在找到与绝大多数行为显着不同的网络元素&#xff08;例如节点、边、子图…

【BASH】回顾与知识点梳理(二)

【BASH】回顾与知识点梳理 二 二. Shell 的变量功能2.1 什么是变量&#xff1f;2.2 变量的取用与设定: echo, 变量设定规则: set/unset2.3 环境变量的功能用 set 观察所有变量 (含环境变量与自定义变量)export&#xff1a; 自定义变量转成环境变量那如何将环境变量转成自定义变…

Tomcat 创建https

打开CMD,按下列输入 keytool -genkeypair -alias www.bo.org -keyalg RSA -keystore d:\ambition.keystore -storetype pkcs12 输入密钥库口令:123456 再次输入新口令:123456 您的名字与姓氏是什么? [Unknown]: www.ambition.com 您的组织单位名称是什么? [Unknown…

PostgreSQL Patroni_exporter 监控 patroni高可用工具

Patroni是Cybertec公司基于python语言开发的&#xff0c;可用于使用流复制来创建&#xff0c;管理&#xff0c;维护和监视高可用性PostgreSQL集群设置的工具。 目前&#xff0c;PatroniEtcd 是最为推荐的PostgreSQL数据库高可用方案之一。 PostgreSQL有postgres_exporter监控采…

ssl单向证书和双向证书校验测试及搭建流程

零、前提准备 说明&#xff1a; 50.50.1.118作为服务端&#xff0c;系统是 linux&#xff0c;openssl版本是&#xff1a;OpenSSL 1.1.1f 31 Mar 2020。 50.50.1.116是客户端&#xff0c;系统是Windows&#xff0c;openssl版本是&#xff1a;OpenSSL 3.0.5 5 Jul 2022 (Library…

BES 平台 SDK之LED的配置

本文章是基于BES2700 芯片&#xff0c;其他BESxxx 芯片可做参考&#xff0c;如有不当之处&#xff0c;欢迎评论区留言指出。仅供参考学习用&#xff01; BES 平台 SDK之代码架构讲解二_谢文浩的博客-CSDN博客 关于SDK 系统框架简介可参考上一篇文章。链接如上所示&#xff01…

CSS font-family 等宽字体

CSS font-family 等宽字体 font-family: "Lucida Console", Consolas, "Courier New", Courier, monospace; font-family: Courier New, Courier, Lucida Console, Consolas, monospace; font-family: Courier, Lucida Console, Consolas, Courier New,…

JavaWeb(8)——前端综合案例2(节流和防抖)

目录 一、节流和防抖概念 二、实例演示 三、需要注意的 一、节流和防抖概念 二、实例演示 Lodash 简介 | Lodash中文文档 | Lodash中文网 (lodashjs.com) <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><m…

【Redis】内存数据库Redis进阶(搭建各种集群)

目录 单机安装Redis搭建Redis主从集群搭建Redis哨兵集群 基于 CentOS 7 的 Redis 集群 单机安装Redis 安装 Redis 所需要的依赖&#xff1a; yum install -y gcc tcl将 Redis 安装包&#xff08;redis-6.2.4.tar.gz&#xff09;上传到任意目录 解压缩&#xff1a; tar -xzf …

Java NIO 详解

Java 从1.4开始引入NIO&#xff08;New IO&#xff09;&#xff0c;是一种基于块&#xff08;Block&#xff09;的IO机制&#xff0c;也称为非阻塞IO。相比于传统的Java IO&#xff08;IO流&#xff09;方式&#xff0c;Java NIO提供了更快速、高效、灵活的IO操作。 Java NIO的…

Aduino中eps环境搭建

这里只记录Arduino2.0以后版本&#xff1a;如果有外网环境&#xff0c;那么可以轻松搜到ESP32开发板环境并安装&#xff0c;如果没有&#xff0c;那就见下面操作&#xff1a; 进入首选项&#xff0c;将esp8266的国内镜像地址填入&#xff0c;然后保存&#xff0c;在开发板中查…

代码随想录算法训练营第二十九天 | Leetcode随机抽题检测

Leetcode随机抽题检测 160 相交链表未看解答自己编写的青春版重点题解的代码206 反转链表 一段用于复制的标题未看解答自己编写的青春版重点题解的代码日后再次复习重新写 234 回文链表未看解答自己编写的青春版重点综上&#xff0c;利用快慢指针找寻链表中间&#xff0c;就按加…

牛客网Verilog刷题——VL51

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

JVM基础篇-方法区与运行时常量池

JVM基础篇-方法区与运行时常量池 方法区 Java 虚拟机有一个在所有 Java 虚拟机线程之间共享的方法区。方法区类似于传统语言的编译代码的存储区或者类似于操作系统进程中的“文本”段。它存储每个类的结构&#xff0c;例如运行时常量池、字段和方法数据&#xff0c;以及方法和…

Hadoop 集群如何升级?

前言 本文隶属于专栏《大数据技术体系》&#xff0c;该专栏为笔者原创&#xff0c;引用请注明来源&#xff0c;不足和错误之处请在评论区帮忙指出&#xff0c;谢谢&#xff01; 本专栏目录结构和参考文献请见大数据技术体系 正文 升级 Hadoop 集群需要细致的规划&#xff0c;特…

使用docker部署Wordpress

文章目录 1.创建网络2.创建volume存储3.拉取镜像4.创建mysql容器mysql修改密码 5.创建wordpress容器6.访问localhost:80就可以直接使用啦 1.创建网络 docker network create --subnet172.18.0.0/24 pro-net2.创建volume存储 # mysql 存储 docker volume create volume_mysql…

vscode 前端开发插件 2023

自己记录 安装vscode后必装插件 chinesegit 必装没啥可说 随时更新 1.CSS Navigation CTRL点击类名可跳转到对应样式位置。 如果是scss less的话。css peak插件无法生效 2.GitLens — Git supercharged 可以看到每一行的git提交记录。 3.Auto Rename Tag 可以同步更新…