C++——list(2)

news2024/11/26 3:29:16

作者:几冬雪来

时间:2023年9月28日

内容:C++——list内容讲解

目录

前言: 

list的const迭代器: 

const的iterator: 

const迭代器: 

operator->: 

拷贝构造:

迭代器接口补充: 

代码: 

结尾: 


前言: 

在上一篇博客中我们讲解了list的新接口,list与vector,string的区别所在。更加进一步的说明了list的迭代器的书写,而在今天我们将借由list迭代器来对list的const迭代器进行详细的讲解与说明。 

list的const迭代器: 

在学习list的const迭代器之前,我们先来回顾一下list的迭代器。

list,string和vector的迭代器访问的是指针,所以所有的容器都希望提供像指针一样去访问容器的方式。 

在string和vector只需要提供原生指针即可。

string和vector使用的原生指针,在这里typedef T*就是iterator,因为普通的指针完美的符合这个行为

并且string和vector的物理空间是连续的

因此它的解引用访问的就是该处的数据,进行++就是访问下一个数据。 

const的iterator: 

那么在这里,const的iterator是什么,它使用的指针和vector,string有什么不同

相较于二者,const的iterator有所不同。 

在const的使用中分为两种,一种是指针不能被修改,另外一种是指向的内容不能被修改

像上图在*之前的const修饰的都是指向的内容不能被修改,都是它自己是可以被修改的。所以的迭代器都要符合

string和vector的不同是因为二者的底层结构占了便宜,因为它们的空间是连续的,而list则是不连续的

const迭代器: 

在list中因为空间是不连续的,因此它的指针不能直接使用。这个时候就要借助一个类来封装,来控制它的行为。 

那么如果控制它的行为呢?我们可以重载它的operator*和operator++,这样就能控制它这里的行为

在这里我们就将封装的迭代器类型进行一个typedef来达到我们的目标

接下来我们进行讲解const迭代器,这里我们要使用的const是指向的内容不能被修改,而它自己本身是可以被修改的。 

这个地方很多人都会这样去写。 

普通迭代器的对象前加上一个const,这里的const影响的是我们的对象,对象不能被修改。因此在后面运行代码的时候就不能调++等操作。 

既然这条路走不通,那么我们该如何实现呢?这个地方就需要去控制operator*来达到这个目的

为了解决问题,在定义处我们又定义了一个新的类,这个新的类和原先的类基本一致。唯一有不同的点就是在operator*处

那么是哪个地方不同,我们就来对比一下。 

对比两个类中的operator*,我们不难看出二者的返回值不一样,新的类对比原先的类,在返回值处多了一个const

但是如果真的要这样写的话,那就太冗余了。 

这里有没有什么好办法呢?是有的,在这里我们可以通过一个类型去控制这个返回值,也就是增加一个模板参数。 

类似这里,我们就添加了一个模板参数,同时将operator*的返回值更改为Ref。那么这里的Ref是什么呢

从上面的类模板中我们是不知道的,这个时候就要看下面的iterator的书写了。

在这个地方普通的iterator的Ref代表的是T引用,而const iterator在这个地方则是代表的const T引用。 

这也就完成了想要的通过一个类模板给不同的实例化,其实这里本质上我们还是写了两个类,因为我们给了不同的模板参数

同时因为是不同的类,因此原先的类就不能使用了,在这个地方就需要我们对新的类进行typedef一下

这里将新的类typedef为self,同时在下面operator++和operator*等涉及到这个返回值的都要对其进行修改

operator->: 

接下来来讲解一下list中的operator->接口

这个地方有人就要问了,在我们还在学C语言的时候不是有讲解过->和&之间的关系,虽然书写的代码不同,但是结果是相同的。

在list迭代器中刚刚书写了解引用的操作,为什么还要加入->? 

为了解答问题,在这里我们写了一个链表,链表的类型是自定义类型。然后接下来我们要对类型进行访问。 

然后来看一下访问的代码。 

在这里我们迭代器模拟的是一个指向结构的指针。 

因为上面迭代器的类型是自定义类型,因此这个地方数据模拟的就是自定义类型的指针。所以代码就要用到->了。 

因此上面的这一串代码就能改编为下面这样。

而因为在这里使用了->,所以在迭代器中我们要加入operator->来辅助我们完成

那么再将它的代码写出来。

先比较与我们operator解引用时候书写的代码,operator->明显有着不同之处。首先是返回值,解引用的返回值是Ref,而->的返回值是T*

同时二者return的内容也是不同,->与解引用相比多了一个&符号

这两种情况就导致了operator->的返回值略显奇怪。

根据上图进行解释。 

operator*和operator->处return的_node->_val的值都是我们这里自定义类型A。但是因为返回值的不同,解引用的代码可以装换为对A的解引用,然后就能访问它里面的值

operator->不同,它的返回值是T*转换的结果是A*也就是A的指针,而我们没办法通过A*去访问A里面的值

因此理论上来说正确的代码应该这样写。 

这个地方需要两个->才能真正访问我们里面的数据,第一个->是去调用operator->返回的是A*, 然后第二个->就是去访问A里面的值

但是如果是这样书写的话,可读性极差。而我们的运算符重载正好要求可读性,那么编译器就进行了一个特殊处理,在这里省略了一个->

那么接下来一个问题就是它的const该怎么写,案例来说这里的返回值是const T*。 

 

解决这个问题的话,我们可以新增加一个模板参数来区分它们。 

拷贝构造:

接下来我们来讲解list的拷贝构造

通过两篇博客我们可以发现list的迭代器和vector与string多多少少有些不同,但是它们有一点是相同的,那就是在拷贝构造的时候都会出现深浅拷贝的问题。 

为了处理深浅拷贝的问题,在这个地方如果要将lt1给lt2,首先就要开空间,给出各个指向的是什么

开好空间之后,接下来就要遍历数据了。在这个地方有一个要注意的点,那就是正常情况下因为不清楚T的对象不确定是vector,string还是其他的,因此在遍历条件处要加引用操作符。 

最后再将lt1里面的数据插入到lt2,这样就能完成我们的深拷贝了。 

同样的我们也可以对我们的代码进行优化。 

因为在list迭代器中多次运用到开空间的操作,因此我们可以将它单独拿出来写一个接口,方便以后的调用。 

迭代器接口补充: 

再在最后,将我们平时迭代器用到的代码,比如erase,insert等等都写上,到这里我们的迭代器就正式完成了。 

代码: 

using namespace std;
#include<>
#pragma once  
#include<assert.h>

namespace bit
{
	template<class T>
	struct list_node
	{
		list_node<T>* _next;
		list_node<T>* _prev;
		T _val;
	
		list_node(const T& val = T())
			:_next(nullptr),
			_prev(nullptr),
			_val(val)
		{

		}
	}; 

	template<class T,class Ref,class Ptr>
	struct __list_iterator
	{
		typedef list_node<T> Node;
		typedef __list_iterator<T, Ref> self; 
		Node* _node;
		__list_iterator(Node* _node)
			:_node(node)
		{

		}   

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

		iterator end()
		{
			return _head;
		}

		Ref operator*()
		{
			return _node->_val;
		}

		self& operator++()
		{
			_node = _node->_next;
			return *this;
		}

		Ptr operator->()
		{
			return &_node->_val;
		}

		self operator++(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 Ref& it)
		{
			return _node != it._node;
		}

		bool operator==(const Ref& it)
		{
			return _node == it._node;
		}
	};

	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;

		/*typedef const __list_iterator<T> const_iterator;*/

		list()
		{
			empty_init()
		}

		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}

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

		list(const list<T>& lt)
		{
			empty_init();

			for (auto& e : lt)
			{
				push_back(e);
			}
		}

		void swap(list<T>& lt)
		{
			std::swap(_head, lt._head);
			std::swap(_size, lt._size);
		}

		list<T>& operator=(list<T> lt)
		{
			swap(lt); 
			return *this;
		}

		void clear()
		{ 
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}
		}

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

		void push_front(const T& x)
		{
			insert(begin(), x);
		}

		void pop_back()
		{
			erase(--end());
		}

		void pop_front()
		{
			erase(begin()); 
		}



		iterator insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* newnode = new Node(x);

			prev->_next = newnode;
			newnode->_next = cur;

			cur->_prev = newnode;
			newnode->_prev = prev; 

			return newnode;
		}

		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 next;

		}

	private:
		Node* _head;
	};


}

结尾: 

到这里,我们的list也就告一段落了。大部分知识再看一次是不够的,很多都可以要自己画图对其进行理解,并且list的一些知识到后面学习的时候还大有用处,它也是我们C++一个重要的板块,最后希望这篇博客能为学习的各位提供帮助。 

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

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

相关文章

LayoutLMv2:多模态预训练用于富含视觉元素的文档理解【论文翻译】

文章目录 基础信息摘要1 Introduction2 Approach2.1 Model Architecture2.2 2.2 Pre-training TasksMasked Visual-Language ModelingText-Image Alignment (对齐)Text-Image Matching(匹配) 3 Experiments3.1 Data 3.2 SettingsPre-training LayoutLMv2Fine-tuning LayoutLMv2…

初探802.11协议(5)——MIMO/MU-MIMO/OFDMA概念介绍

目录 一. MIMO 1.1 从SISO到MIMO 1.1.1 SISO 1.1.2 SIMO 1.1.3 MISO 1.1.4 MIMO 1.2 MIMO类型 1.3 Wi-Fi MIMO 1.3.1 空间分集 1.3.2 空分复用 二. 从MIMO到MU-MIMO 三. OFDM到OFDMA 四. MU-MIMO vs OFDMA REF 一. MIMO 空间流 (Spatial Stream) &#xff1a;传…

一个简单的敏捷开发的例子

敏捷开发是一种以人为核心、迭代、循序渐进的软件开发方法。 敏捷开发以用户的需求进化为核心&#xff0c;采用迭代、循序渐进的方法进行软件开发。在敏捷开发中&#xff0c;软件项目在构建初期被切分成多个子项目&#xff0c;各个子项目的成果都经过测试&#xff0c;具备可视…

【数据库】存储引擎InnoDB、MyISAM、关系型数据库和非关系型数据库、如何执行一条SQL等重点知识汇总

目录 存储引擎InnoDB、MyISAM的适用场景 关系型和非关系型数据库的区别 MySQL如何执行一条SQL的 存储引擎InnoDB、MyISAM的适用场景 InnoDB 是 MySQL 默认的事务型存储引擎&#xff0c;只有在需要它不支持的特性时&#xff0c;才考虑使用其它存储引擎。实现了四个标准的隔…

Spring事务不生效的场景的解决方案

一、前言 在Java Web开发中&#xff0c;使用Spring框架可以大大简化开发人员的工作。其中&#xff0c;事务管理是Spring框架中的一个重要功能&#xff0c;它可以确保多个数据库操作要么全部成功&#xff0c;要么全部失败。但是&#xff0c;在实际开发中&#xff0c;我们可能会…

【SQLServer语句按月记录总数量】

情景描述&#xff1a;如下表Table_Name有id,name,CreatedDate三个字段.现在需要按照月份记录总数量&#xff0c;得到一个包含月份&#xff0c;数量的表。 select MONTH(CreatedDate) as 月份, COUNT(*) as 数量 from Table_Name group by MONTH(CreatedDate) order by month(C…

JVM调优实战及常量池详解

阿里巴巴Arthas详解 Arthas 是 Alibaba 在 2018 年 9 月开源的 Java 诊断工具。支持 JDK6&#xff0c; 采用命令行交互模式&#xff0c;可以方便的定位和诊断线上程序运行问题。Arthas 官方文档十分详细&#xff0c;详见&#xff1a;https://alibaba.github.io/arthas Arthas…

前端uniapp防止页面整体滑动页面顶部以上,设置固定想要固定区域宽高

解决&#xff1a;设置固定想要固定区域宽高 目录 未改前图未改样式改后图改后样式 未改前图 未改样式 .main {display: flex;flex-direction: row;// justify-content: space-between;width: 100vw;// 防止全部移动到上面位置&#xff01;&#xff01;&#xff01;&#xff01…

排序:堆排序算法分析以及插入删除操作

堆排序可以看作顺序存储的完全二叉树。 堆排序属于选择排序的一种&#xff0c; 选择排序:每一趟在待排序元素中选取关键字最小(或最大&#xff09;的元素加入有序子序列。 1.堆的定义 若n个关键字序列 L [ 1... n ] L[ 1...n] L[1...n]满足下面某一条性质&#xff0c;则称为堆…

【Vue】监控路由与路由参数, 刷新当前页面数据的几种方法

目录 一、Vue监控路由 1、Vue中watch监控路由 2、Vue中watch监控路由的某一个参数 3、Vue中watch同时监控多个路由 二、刷新当前页面数据 1、location.reload 2、$router.go(0) 3、this.$router.resolve()与this.$router.resolve() a、this.$router.resolve() b、thi…

leetcode1610. 可见点的最大数目(java)

可见点的最大数目 题目描述滑动窗口 题目描述 难度 - 困难 leetcode1610. 可见点的最大数目 给你一个点数组 points 和一个表示角度的整数 angle &#xff0c;你的位置是 location &#xff0c;其中 location [posx, posy] 且 points[i] [xi, yi] 都表示 X-Y 平面上的整数坐标…

网络层五大核心知识点

引言 在前面几期文章中&#xff0c;无论是UDP还是TCP&#xff0c;其实我们都在介绍 TCP/IP 模型的“传输层”&#xff0c;我们知道&#xff0c;数据在传输层完成相应的封装后就会来到网络层进行下一步的数据转发&#xff0c;那么数据在网络层又接受了哪些神秘的力量&#xff1…

心法利器[102] | 大模型落地应用架构的一种模式

心法利器 本栏目主要和大家一起讨论近期自己学习的心得和体会&#xff0c;与大家一起成长。具体介绍&#xff1a;仓颉专项&#xff1a;飞机大炮我都会&#xff0c;利器心法我还有。 2022年新一版的文章合集已经发布&#xff0c;累计已经60w字了&#xff0c;获取方式看这里&…

SAP替代物料的解决方案详解

说明&#xff1a; SAP中的替代有三种&#xff1a; A物料和B物料可以互相替代&#xff0c;但是由物料计划部门自由控制使用A还是使用B;在某个产品中&#xff0c;当物料A不够时&#xff0c;才用B&#xff1b;当物料A用完后&#xff0c;将永远不再用&#xff0c;而是用物料B取代A…

CYEZ 模拟赛 7

A 弹珠 妙妙题。 先每个组分一个小球。等价于 n − k n-k n−k 拆分为任意个 [ 1 , k ] [1,k] [1,k] 的数的方案数。 本质是根据面积的转换&#xff0c;直观解释&#xff1a; 完全背包即可。代码。 B C 总结

机器人制作开源方案 | 家庭清扫拾物机器人

作者&#xff1a;罗诚、李旭洋、胡旭、符粒楷 单位&#xff1a;南昌交通学院 人工智能学院 指导老师&#xff1a;揭吁菡 在家庭中我们有时无法到一些低矮阴暗的地方进行探索&#xff0c;比如茶几下或者床底下&#xff0c;特别是在部分家庭中&#xff0c;如果没有及时对这些阴…

设计加速!11个Adobe XD插件推荐!

你是否一直在寻找可以提升 Adobe XD 工作流程和体验的方法&#xff1f;如果是&#xff0c;一定要试试这些 Adobe XD 插件&#xff01;本文将介绍 11 款好用的 Adobe XD 插件&#xff0c;这些插件可以为 UI/UX 设计添加很酷的新功能&#xff0c;极大提升你的工作效率和产出。让我…

Linux 常用基本命令

1.走近Linux系统 开机登录 开机会启动许多程序。它们在Windows叫做"服务"&#xff08;service&#xff09;&#xff0c;在Linux就叫做"守护进程"&#xff08;daemon&#xff09;。 关机 在linux领域内大多用在服务器上&#xff0c;很少遇到关机的操作。毕竟…

大模型深挖数据要素价值:算法、算力之后,存储载体价值凸显

文 | 智能相对论 作者 | 叶远风 18.8万亿美元&#xff0c;这是市场预计2030年AI推动智能经济可产生的价值总和&#xff0c;其中大模型带来的AI能力质变无疑成为重要的推动力量。 大模型浪潮下&#xff0c;业界对AI发展的三驾马车——算力、算法、数据任何一个维度的关注都到…

23 mysql index 查询

前言 这里主要是 探究一下 explain $sql 中各个 type 诸如 const, ref, range, index, all 的查询的影响, 以及一个初步的效率的判断 这里会调试源码来看一下 各个类型的查询 需要 lookUp 的记录 以及 相关的差异 此系列文章建议从 mysql const 查询 开始看 测试表结构…