C++初阶之list的使用和模拟以及反向迭代器的模拟实现

个人主页:点我进入主页

专栏分类:C语言初阶  C语言进阶  数据结构初阶    Linux    C++初阶    算法

欢迎大家点赞,评论,收藏。

一起努力,一起奔赴大厂

一.list简介

        list是一个带头双向链表,在数据结构的时候,我写过关于带头双向循环链表的实现,可以看博客https://yangking.blog.csdn.net/article/details/134495668,我们可以看下面的图,是list的存储结构,

本次的内容包括list的使用,list的模拟实现,list的迭代器以及反向迭代器的原理,模拟实现和使用,最重要的是迭代器和反向迭代器的内容,在前面string和vector中迭代器是原生的指针,但是在这里不是,具体是什么样子的我们可以看后面的内容。 

二.一个简易的list

2.1.单个节点

template<class T>
struct ListNode
{typedef  ListNode<T>  Node;Node* _next;Node* _prev;T _data;ListNode(const T& val = T()): _next(nullptr), _prev(nullptr), _data(val){}
};

在这里我们可以使用strcut结构体来创建一个节点

2.2.默认构造

void empty_init()
{_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;
}
list()
{empty_init();
}

我们创建一个头节点, 让它指向自己。

2.3push_back

void push_back(const T& val)
{Node* newnode = new Node(val);Node* prev = _head._node->_prev;Node* cur = _head._node;prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;_size++;
}

在这里我们就是简单的插入操作,不给具体的解释。到这里我们简易的list就完成了,我们依次插入1234可以看到

void test()
{list<int> l;l.push_back(1);l.push_back(2);l.push_back(3);l.push_back(4);
}

三.迭代器

        我们封装一个迭代器的类,我们的迭代器需要支持

list<int>::iterator it = lt1.begin();
while (it != lt1.end())
{cout << *it << " ";++it;
}
cout << endl;

所以我们里面需要包括!=,*it,++it,以及begin和end这些内容,我们需要知道迭代器模仿的是Node*这个行为,我们看一下模拟代码:

template <class T>
struct ListIterator
{typedef ListNode<T>  Node;typedef ListIterator<T> Self;Node* _node;ListIterator(Node* node):_node(node){}bool operator==(const Self& it){return _node == it._node;}bool operator!=(const Self& it){return _node != it._node;}Self& operator--(){_node = _node->_prev;return *this;}Self operator--(int){Self tmp(_node);_node = _node->_prev;return *tmp;}Self& operator++(){_node = _node->_next;return *this;}Self operator++(int){Self tmp(_node);_node = _node->_next;return tmp;}T& operator*(){return _node->_data;}
};

我们在list类里面加入

iterator begin()
{return _head->_next;
}
iterator end()
{return _head;
}

我们运行测试代码

void test()
{list<int> l;l.push_back(1);l.push_back(2);l.push_back(3);l.push_back(4);list<int>::iterator it = l.begin();while (it != l.end()){cout << *it << " ";it++;}cout << endl;
}

运行结果为

四.insert和erase以及复用

4.1insert

 在这里我们的insert用迭代器进行插入,给以个位置,插入它的前面,

 我们的代码如下:

void insert(iterator pos, const T& val)
{Node* newnode = new Node(val);Node* prev = pos._node->_prev;Node* cur  = pos._node;prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;_size++;
}

有了我们的insert我们的push_back可以复用我们的insert,可以改为

void push_back(const T& val)
{insert(end(), val);
}

在这里我们的insert不会造成迭代器失效,因为指针指向的位置不会发生改变,它是插入到pos位置的前面。

4.2erase以及迭代器失效

对于返回值为什么是iterator,这和我们的迭代器失效有关,我们先用void来展示其中的问题。

void erase(iterator pos)
{Node* prev = pos._node->_prev;Node* next = pos._node->_next;delete pos._node;prev->_next = next;next->_prev = prev;_size--;
}

我们的测试代码如下:
 

void test()
{list<int> l;l.push_back(1);l.push_back(2);l.push_back(3);l.push_back(4);list<int>::iterator it = l.begin();while (it != l.end()){if (*it % 2 == 0) l.erase(it);else it++;}it = l.begin();while (it != l.end()){cout << *it << " ";it++;}cout << endl;
}

在这里会出现错误,原因是迭代器失效,野指针的引用,所以我们需要对迭代器进行维护,所以有了返回值,我们改为

iterator erase(iterator pos)
{Node* prev = pos._node->_prev;Node* next = pos._node->_next;delete pos._node;prev->_next = next;next->_prev = prev;_size--;return next;
}

4.3复用

有了这两个,我们的pop_back,pop_front,push_front就可以有了

void pop_back()
{erase(--end());
}
void pop_front()
{erase(begin());
}
void push_front(const T& val)
{insert(begin(), val);
}

五.operator->()

        我们的T是一个结构体

struct A
{int a = 1;int b = 2;
};
void test()
{list<A> l;l.push_back({ 1,2 });l.push_back({ 2,3 });l.push_back({ 3,4 });l.push_back({ 4,5 });list<A>::iterator it = l.begin();while (it != l.end()){/*	cout << (*it).a << " " << (*it).b << endl;*/cout << it._node->_data.a << " " << it._node->_data.b << endl;it++;}
}

 我们的解引用是不是看的非常难受,所有我们需要operator一下,

T* operator->()
{return &_node->_data;
}

 我们改为

cout << it->a << " " << it->b << endl;

六.const迭代器

        有了我们的非const版本的,那么我们就需要我们的const版本的,const版本就是将所有的都复制一份,然后operator*返回const的

template <class T>
struct ListConstIterator
{typedef ListNode<T>  Node;typedef ListConstIterator<T> Self;Node* _node;ListConstIterator(Node* node):_node(node){}bool operator==(const Self& it){return _node == it._node;}bool operator!=(const Self& it){return _node != it._node;}Self& operator--(){_node = _node->_prev;return *this;}Self operator--(int){Self tmp(_node);_node = _node->_prev;return *tmp;}Self& operator++(){_node = _node->_next;return *this;}Self operator++(int){Self tmp(_node);_node = _node->_next;return tmp;}const T& operator*(){return _node->_data;}const T* operator->(){return &_node->_data;}
};

由于这个会和非fonst很多内容一样,会造成代码冗余,所以我们可以利用模板来简化我们的代码,我们非const的代码函数的返回值为Self,T&,T*,const版本的是Self,const T&,const T*,所以我们可以写成模板

template <class T,class Ref,class Ptr>
struct ListIterator
{typedef ListNode<T>  Node;typedef ListIterator< T, Ref, Ptr> Self;Node* _node;ListIterator(Node* node):_node(node){}bool operator==(const Self& it){return _node == it._node;}bool operator!=(const Self& it){return _node != it._node;}Self& operator--(){_node = _node->_prev;return *this;}Self operator--(int){Self tmp(_node);_node = _node->_prev;return *tmp;}Self& operator++(){_node = _node->_next;return *this;}Self operator++(int){Self tmp(_node);_node = _node->_next;return tmp;}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}
};

我们在list类里面写入

typedef ListIterator<T,T&,T*> iterator;
typedef ListIterator<T,const T&,const T*> const_iterator;

这样我们由我们自己写改为了编译器去写。

七.反向迭代器

        我们的反向得带起是使用正向迭代器进行写的,它的rbegin是正向迭代器的end,rend是正向迭代器的begin,所以它的operator*是解引用前一个的。

template <class Iterator, class Ref, class Ptr>
struct ReverseIterator
{typedef ReverseIterator<Iterator, Ref, Ptr> Self;Iterator _it;ReverseIterator(Iterator it):_it(it){}Ref operator*(){Iterator tmp = _it;--tmp;return *tmp;}Ptr operator->(){return _it.operator->();}Self rbegin(){return ReverseIterator(_it.end());}Self rend(){return ReverseIterator(_it.begin());}Self operator++(){--_it;return *this;}Self operator++(int){Iterator tmp = _it;--_it;return tmp;}Self operator--(){++_it;return *this;}Self operator--(int){Iterator tmp = _it;tmp = _it;++_it;return tmp;}bool operator!=(Self it){return _it != it._it;}};

到这里我们的内容结束了。

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

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

相关文章

生成ssh来连接git

生成SSH密钥&#xff1a; 打开你的命令行终端&#xff08;如Windows的CMD、PowerShell&#xff0c;或者Linux/Mac的Terminal&#xff09;。 运行以下命令来生成SSH密钥对&#xff08;私钥和公钥&#xff09;&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexampl…

c++ 的线程是个对象吗

在C中&#xff0c;线程通常不是直接通过对象来表示的&#xff0c;但C11及以后的标准引入了对线程的高级抽象&#xff0c;主要是通过<thread>库中的std::thread类来实现的。因此&#xff0c;可以说std::thread是一个类&#xff0c;其实例&#xff08;对象&#xff09;表示…

10个SpringMVC的核心组件详解

Spring MVC 的核心组件是构成整个框架的基础&#xff0c;它们协同工作以支持基于 MVC 架构的 Web 应用程序开发。以下是V哥工作中整理的每个组件的详细介绍&#xff0c;包括示例代码和解释&#xff1a; 1. DispatcherServlet&#xff1a; 作用&#xff1a;作为前端控制器&…

不必追求深度,浅尝辄止为宜

近日笔者撰文称&#xff0c;有幸应《百度-百家号》相邀&#xff0c;在其发起的《征文任务》栏目中写作深度文章&#xff0c;便试着开头写了一篇《万科有“活下去”的可能性吗&#xff1f;》的时评文章&#xff0c;于5月3日发表&#xff0c;舆情反映不错&#xff0c;不到三天时间…

python菜鸟级安装手册

python安装教程 电脑-右键-属性&#xff0c;确认系统类型和版本号&#xff0c;比如本案例系统是64位 win10 点击python官网&#xff0c;进行下载 适用于 Windows 的 Python 版本 |Python.org 选择第一个安装程序64位即可满足需要&#xff0c; 嵌入式程序包是压缩包版本&…

JavaScript中的RegExp和Cookie

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;JavaScript 精粹 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 &#x1f506;RegExp &#x1f3b2; 1 什么是正则表达式 &#x1f3b2;2 创建…

山东省文史书画研究会成立20周年系列活动徽标征集胜选名单公布

2024年5月1日&#xff0c;山东省文史书画研究会成立20周年系列活动徽标征集落下帷幕。征稿启事下发后&#xff0c;得到社会各界人士的广泛关注与参与&#xff0c;共收到设计方案608件。经过初评&#xff0c;选出5幅作品进入复评&#xff0c;并经过网络投票和专家投票相结合的方…

下载阿里云服务器的文件

阿里云服务器是一种强大的云计算服务&#xff0c;为用户提供了稳定可靠的计算资源。在使用阿里云服务器时&#xff0c;有时需要下载服务器上的文件。本文将介绍如何在阿里云服务器上下载文件的方法。 步骤一&#xff1a;登录阿里云服务器 首先&#xff0c;你需要登录到你的阿里…

颗粒精炼剂可用于铝及铝合金熔铸工艺中 我国生产企业众多

颗粒精炼剂可用于铝及铝合金熔铸工艺中 我国生产企业众多 颗粒精炼剂指外观呈白色粉末状或颗粒状&#xff0c;可用于金属颗粒表面处理的重要化学药剂。颗粒精炼剂具有反应速度快、绿色环保、安全稳定性好等优势&#xff0c;在铝及铝合金的熔铸工艺中应用较多。按照钠含量不同&a…

有关string的部分接口

1.迭代器与反向迭代器(iterator-) 迭代器是可以用来访问string里面的内容的&#xff0c;这里来记录一下使用的方法。 里面用到了一个叫做begin函数和一个end函数&#xff0c;这两个都是针对string使用的函数。 s1.begin()函数是指向string内容的第一个元素 而s1.end()指向的则…

flask笔记-1: 路由

启动服务 绑定host flask run --host0.0.0.0 调试模式 flask --app hello run --debug 路由 创建路由 1. 装饰器 app.route(/one,methods[GET,POST]) 2. add_url_rule app.add_url_rule(/one,view_funcone)

每日OJ题_贪心算法三②_力扣553. 最优除法

目录 力扣553. 最优除法 解析代码 力扣553. 最优除法 553. 最优除法 难度 中等 给定一正整数数组 nums&#xff0c;nums 中的相邻整数将进行浮点除法。例如&#xff0c; [2,3,4] -> 2 / 3 / 4 。 例如&#xff0c;nums [2,3,4]&#xff0c;我们将求表达式的值 "…

滑块验证码说明

滑块验证码说明 滑块验证码 旋转验证码 滑动还原验证码 文字点选验证码 快速上手 注意: 如果你项目是使用的Springboot&#xff0c; 请使用SpringBoot脚手架工具tianai-captcha-springboot-starter; 该工具对验证码进行了封装&#xff0c;使其使用更加方便快捷 后端说明 引…

深度学习之基于Matlab Googlenet网络男女性别识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景 随着计算机视觉技术的快速发展&#xff0c;性别识别在多个领域中都展现出了广泛的应用前景&#xff…

【Unity】如何获得TMP Button下的text内容

【背景】 unity项目中使用了TMP命名空间的Button UI组件。脚本中需要获得Button下Text的内容,但是发现用TextMeshPro仍然无法获得button下的text对象。 【分析】 Hierarchy结构上看明确Button下是有Text组件的: 括号里是TMP,所以理论上用TextMeshPro类型去FindComponent…

“A”分考试经验分享:云计算HCIE考试请注意这几点...

大家好&#xff0c;我是誉天云计算HCIE的王同学&#xff0c;于4月2日"A"分通过了云计算3.0 HCIE的认证考试。 首先感谢誉天教育对我的辅导&#xff0c;感谢苗苗老师和石老师对我的帮助&#xff0c;通过这次考试让我对华为云计算有了一定的了解。接下来我就与大家分享…

a标签隐藏页面来源,去除referer引用

<a hreflink relnoreferrer/> rel属性详解&#xff1a; noreferrer 不发送跳转页面来源

bitnami/minio容器部署记录

文章目录 说明minio容器部署创建目录和文件启动容器命令访问控制台 说明 如果你困惑于文件最后修改时间和代码程序查询结果不一致&#xff08;相差八小时&#xff09;请参看Minio(官方docker版)容器部署时区问题研究记录注意创建/opt/1panel/apps/minio/data/后&#xff0c;一…

GOG平台账号注册教程 内附GOG平台官网地址

GOG平台账号注册教程 内附GOG平台官网地址 GOG平台不知道大家听没听说过&#xff0c;该平台也是一款游戏平台&#xff0c;上面还是有着不少的游戏的&#xff0c;尤其是该平台的福利活动特别多&#xff0c;经常会免费发放一些游戏&#xff0c;这个7月份的话就有两款游戏现在是…

聊聊 ASP.NET Core 中间件(二):中间件和筛选器的区别

前言 有些小伙伴看到上一篇文章后&#xff0c;可能会发现中间件和我们之前讲的筛选器非常类似&#xff0c;比如它们都是通过 next 串起来的一系列的组件&#xff0c;并且都可以在请求处理前后执行代码&#xff0c;都可以通过不执行 next 来进行请求的终止。那么筛选器和中间件…