C++:STL-list模拟实现:迭代器的封装

STL-list模拟实现细节

  • 一. 模拟实现的思想细节
    • 1.迭代器实现:用类进行封装
    • 2.++和--的重载
    • 3.奇怪的->重载
    • 4.const迭代器
  • 二.实现源码

一. 模拟实现的思想细节

1.迭代器实现:用类进行封装

为什么不使用原生指针:

​ 相比于vector和string,它们两个的物理存储空间都是连续的,因此它们在实现迭代器时,迭代器会使用++等操作,对于它们都是可以直接使用原始指针来进行模拟的,但是对于我们的list链表,其数据存储在一个一个分散的结点之中,我们在进行迭代器遍历的时候使用++,指针移动到下一个位置,但是那里的空间是否我们已经动态内存申请从而来存储结点是未知的,其次通过*进行解引用打印这个数据时,我们得到的并不是直接存储的数据,而是 得到的一个一个的结点,那么通过使用原生指针就无法满足我们迭代器的行为。

为什么使用类来封装:

​ 通过类和对象中运算符的重载的学习,我们了解到通过运算符重载可以改变一个运算符的行为,既然我们原生指针 *解引用时无法得到我们想要的行为,那么我们可以通过类的运算符重载来改变 *,++等操作符的行为,让它不是只能访问到结点而实直接访问到结点内部存储的数据,因此我们需要将结点的指针通过类进行封装,从而达到我们的目的。

2.++和–的重载

注意点:迭代器通过++和–改变时,本质是在改变该迭代器内部结点指针的指向,而不是修改这个迭代器本身(即this指针指向的内容)。

在这里插入图片描述

在这里插入图片描述

3.奇怪的->重载

这里肯定会有小伙伴产生疑问,我们->之后得到的应该直接是该结点存储的_val,但是为什么实现的时候返回的是其的地址呢?

这里的主要目的是增加可读性。

首先先观察部分实现源码:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

我们看到在STL库中,->的重载是对operator*()的返回值取地址,而了解到 * 的重载重载之后的行为是返回所存储值本身,即迭代器内部结点内存储的值。,那么我们->之后返回的是operator *()返回值的地址,在逻辑上我们使用->时后应该需要再加一个->才能拿到最终的数据。

下面来看这段测试代码:

在这里插入图片描述

it->调用了operator->(),并且返回了存储值的地址,但是我们我们并没有按照我们所想的增加多写一个->来解引用。

如果我们显示写出->重载函数的调用,我们可以看到是有两个->,其中一个用来调用operator->(),另外一个用来对其返回值解引用,但是我们通过it->_a1隐式调用operator->()却只有一个箭头,因为在隐式调用时省略了一个->,相比于写两个->,一个箭头可以增强可读性。

首先我们要理解迭代器模拟的是T*的行为!

对于T*,我们直接->就可以访问其数据,但是如果该容器存储的不是int,double这样的数据,而是一个自定义类型,比如如上的A,如果直接重载->时是返回确切的值,只能返回一个,比如返回了_a1,但是 _a2怎么办?因此这种行为是不合适的。

迭代器模拟的是T* 的行为,我们期望直接一个->就可以访问到数据,我们此处的迭代器it可以理解为T*。

对于-List中:>重载时,其的重载格式如下:

在这里插入图片描述

4.const迭代器

​ 在我们封装好自己实现的迭代器iterator之后,只能对于普通迭代器来使用,但是const迭代器如果还使用上面的那一个类的迭代器,就会出现问题:

我们的const对象不能修改,但是我们使用的是普通对象的迭代器,其的权限是可读可写,通过*可以修改const对象的值,因此我们需要实现一个const版本的迭代器,而它们最本质的区别就是在 *,->等进行解引用操作时,对于返回值类型是否加const限制的问题,因此我们可以直接CV普通对象的迭代器,对于其 *和->运算符重载时,对其返回值类型加上const限制,但是这样会造成有两个功能等各方面都相似的类,这样会显的我们的List的迭代器比较的臃肿。

​ 然后它们的区别是==个别函数的返回值类型不同,对于只有类型不同的函数我们可以考虑使用模板。==我们通过将这两个运算符重载函数的返回值使用模板参数来确定其内容,这样就可以实现同一个类对于const对象和非const对象都可以使用啦。我们将模板参数传入的是没有使用const修饰的类型typedef成为iterator,将使用const修饰时模板参数传入使用const修饰的类typedef成为const_iterator,这样通过对象调用时调用的是const_iterator还是iterator从而实现分别调用实现不同的功能。

对于区分iterator和const_iterator,我们只需要通过typedef该类对应的不同的模板参数即可实现区分调用。对于模板参数,我们在调用普通迭代器时,传入的就是普通的对象,对于const对象,调用其const迭代器,然后list迭代器中会自动生成对应的const迭代器供我们使用。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二.实现源码

namespace List
{
	template<class T>
	struct ListNode
	{

		//构造
		ListNode(const T& val = T())
			:_val(val),
			_prev(nullptr),
			_next(nullptr)
		{

		}

		ListNode<T>* _prev;
		ListNode<T>* _next;
		T _val;
		
	};

	//这两个迭代器本身只有返回值类型不同--->类型不同时考虑模板
	//template<class T>
	//class Listiterator
	//{
	//public:
	//	typedef ListNode<T> Node;
	//	typedef Listiterator<T> iterator;
	//	Listiterator(Node* node)
	//		:_node(node)
	//	{
	//	}
	//	bool operator!=(const iterator end)
	//	{
	//		return _node != end._node;
	//	}
	//	//it++
	//	iterator operator++(int)
	//	{
	//		iterator tmp(*this);
	//		//错误写法
	//		//*this = this->_node->_next;
	//
	//		//迭代器++是其内部的节点指针的变化
	//		_node = _node->_next;
	//		return tmp;
	//	}
	//	//++it
	//	iterator operator++()
	//	{
	//		_node = _node->_next;
	//		return *this;
	//	}
	//	//it--
	//	iterator operator--(int)
	//	{
	//		iterator tmp(*this);
	//		_node = _node->_prev;
	//		return tmp;
	//	}
	//	//--it
	//	iterator operator--()
	//	{
	//		_node = _node->_prev;
	//		return *this;
	//	}
	//
	//	T& operator*()
	//	{
	//		return _node->_val;
	//	}
	//
	//	T* operator->()
	//	{
	//		return &_node->_val;
	//	}
	//public:
	//	Node* _node = nullptr;
	//};
	//
	//template<class T>
	//class ConstListiterator
	//{
	//public:
	//	typedef ListNode<T> Node;
	//	typedef ConstListiterator<T> const_iterator;
	//
	//	ConstListiterator(Node* node)
	//		:_node(node)
	//	{
	//
	//	}
	//
	//	bool operator!=(const ConstListiterator end)
	//	{
	//		return _node != end._node;
	//	}
	//
	//	//it++
	//	const_iterator operator++(int)
	//	{
	//		iterator tmp(*this);
	//
	//		//错误写法
	//		//*this = this->_node->_next;
	//
	//		//迭代器++是其内部的节点指针的变化
	//		_node = _node->_next;
	//		return tmp;
	//	}
	//
	//	//++it
	//	const_iterator operator++()
	//	{
	//		_node = _node->_next;
	//		return *this;
	//	}
	//
	//	//it--
	//	const_iterator operator--(int)
	//	{
	//		iterator tmp(*this);
	//
	//		_node = _node->_prev;
	//
	//		return tmp;
	//	}
	//
	//	//--it
	//	const_iterator operator--()
	//	{
	//		_node = _node->_prev;
	//		return *this;
	//	}
	//
	//	 const T& operator*()
	//	{
	//		return _node->_val;
	//	}
	//
	//	const T* operator->()
	//	{
	//		return &_node->_val;
	//	}
	//public:
	//	Node* _node = nullptr;
	//};

	template<class T, class Ref, class Ptr>
	class Listiterator
	{
	public:
		typedef ListNode<T> Node;
		typedef Listiterator<T, Ref, Ptr> iterator;

		Listiterator(Node* node)
			:_node(node)
		{

		}

		bool operator!=(const iterator end)
		{
			return _node != end._node;
		}

		//it++
		iterator operator++(int)
		{
			iterator tmp(*this);

			//错误写法
			//*this = this->_node->_next;

			//迭代器++是其内部的节点指针的变化
			_node = _node->_next;
			return tmp;
		}

		//++it
		iterator operator++()
		{
			_node = _node->_next;
			return *this;
		}

		//it--
		iterator operator--(int)
		{
			iterator tmp(*this);

			_node = _node->_prev;

			return tmp;
		}

		//--it
		iterator operator--()
		{
			_node = _node->_prev;
			return *this;
		}

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

		Ptr operator->()
		{
			return &_node->_val;
		}
	public:
		Node* _node = nullptr;
	};

	//迭代器适配器   支持所有的反向迭代器,只要传入正向迭代器,就可以制造出其的反向迭代器
	template<class iterator, class Ref, class Ptr>
	class reverse_Listiterator
	{
	public:
		typedef reverse_Listiterator<iterator, Ref, Ptr> Self;

		iterator _it;

		reverse_Listiterator(iterator it)
			:_it(it)
		{

		}

		Ref operator*()
		{
			//这样会打印出哨兵位
			//return *_it;
			iterator tmp = _it;

			//必须先让tmp--到
			return *(--tmp);
		}

		Ptr operator->()
		{
			return _it.operator->();
		}

		bool operator!=(const Self& s)
		{
			return _it != s._it;
		}

		Self& operator++()
		{
			--_it;
			return *this;
		}

		Self& operator--()
		{
			++_it;
			return *this;
		}
	};

	template<class T>
	class list
	{
	public:
		typedef ListNode<T> Node;
		//typedef Listiterator<T> iterator;
		//typedef ConstListiterator<T> const_iterator;
		typedef Listiterator<T, T&, T*> iterator;
		typedef Listiterator<T, const T&, const T*> const_iterator;
		typedef reverse_Listiterator<iterator, T&, T*> reverse_iterator;
		typedef reverse_Listiterator<const_iterator, const T&, const T*> const_reverse_iterator;

		
		//constructor and destructor

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

		list()
		{
			empty_init();
		}

		list(int n, const T& val = T())
		{
			empty_init();

			for (int i = 0; i < n; i++)
				push_back(val);

		}

		template<class InputIterator>
		list(InputIterator first, InputIterator last)
		{
			empty_init();
			while (first != last)
			{
				push_back(*first);
				first++;
			}
		}

		list(const list<int>& l)
		{
			empty_init();
			const_iterator it = l.begin();
			while (it != l.end())
			{
				push_back(*it);
				it++;
			}
		}

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

		~list()
		{
			empty();
			delete _head;
		}

		//Iterator
		reverse_iterator rbegin()
		{
			return end();
		}
		reverse_iterator rend()
		{
			return begin();
		}

		iterator begin()
		{
			//单参数构造函数支持隐式类型转换
			return _head->_next;
		}

		//最后一个节点的下一个位置
		iterator end()
		{
			return _head;
		}

		const_iterator begin() const
		{
			return _head->_next;
		}

		const_iterator end() const
		{
			return _head;
		}


		//Modify
		void push_back(const T& val = T())
		{
			//Node* newnode = new Node(val);
			//Node* prev = _head->_prev;

			//prev->_next = newnode;
			//newnode->_prev = prev;
			//newnode->_next = _head;
			//_head->_prev = newnode;
			insert(end(), val);
		}

		void push_front(const T& val = T())
		{
			//Node* newnode = new Node(val);
			//Node* next = _head->_next;

			//_head->_next = newnode;
			//newnode->_prev = _head;
			//newnode->_next = next;
			//next->_prev = newnode;

			insert(begin(), val);

		}

		//_head   1   2
		void pop_front()
		{
			//Node* next = _head->_next->_next;
			//_head->_next = next;
			//next->_prev = _head;
			
			erase(begin());
		}

		void pop_back()
		{
			//Node* prev = _head->_prev->_prev;
			//prev->_next = _head;
			//_head->_prev = prev;
			erase(--end());
			//这里不能使用end() - 1,因为没有重载
			//这里也得用--end(),因为end()指向的是最后一个元素的下一个位置
		}

		//             newnode   pos
		//_head    1              2      3 
		void insert(iterator pos, const T& val = T())
		{
			Node* newnode = new Node(val);
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			//Node* next = cur->_next;

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

			_size++;
		}

		//              pos
		//_head    1     2      3 
		iterator erase(iterator pos)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;

			prev->_next = next;
			next->_prev = prev;
			delete cur;
			cur = nullptr;
			_size--;

			return next;
		}
		
		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				//错因:迭代器失效
				//pop_front();
				//it++;
				it = erase(it);
			}
		}

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

		//List capacity
		size_t size()
		{
			return _size;
		}

		bool empty()
		{
			return _size == 0;
		}


		//Element access:
		T& front()
		{
			if (_head->_next == _head)
				cout << "当前链表没有节点,以下值无效" << endl;
			return _head->_next->_val;
		}

		T& back()
		{
			if (_head->_prev == _head)
				cout << "当前链表没有节点,以下值无效" << endl;
			return _head->_prev->_val;
		}

		const T& front() const
		{
			if (_head->_next == _head)
				cout << "当前链表没有节点,以下值无效" << endl;
			return _head->_next->_val;
		}

		const T& back() const
		{
			if (_head->_prev == _head)
				cout << "当前链表没有节点,以下值无效" << endl;
			return _head->_prev->_val;
		}
	private:
		Node* _head = nullptr;
		size_t _size = 0;
	};
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

9.Godot数组|遍历|静态变量|对象|调试

数组和字典的遍历 数组的概念 数组是一组数据的集合。在程序中负责批量处理数据。数组中的元素可以包括各个类型的数据&#xff0c;也可以对数组内数据类型进行限定。可以通过 数组名【数字】 的形式来访问数组元素&#xff0c;数字 0 代表数组的第一个元素。数组可以通过调用…

《中学科技》是什么级别的刊物?如何投稿?

《中学科技》是什么级别的刊物&#xff1f;如何投稿&#xff1f; 《中学科技》创刊于1976年&#xff0c;由上海世纪出版&#xff08;集团&#xff09;有限公司主管&#xff0c;上海科技教育出版社有限公司主办的省级学术期刊&#xff0c;《中学科技》以传播科技知识、启迪智慧…

蓝桥杯2024年第十五届省赛真题-宝石组合

思路&#xff1a;参考博客&#xff0c;对Ha,Hb,Hc分别进行质因数分解会发现&#xff0c;S其实就等于Ha&#xff0c;Hb&#xff0c;Hc的最大公约数&#xff0c;不严谨推导过程如下&#xff08;字丑勿喷&#xff09;&#xff1a; 找到此规律后&#xff0c;也不能枚举Ha&#xff…

AI容器化部署开发尝试 (一)(Pycharm连接docker,并部署django测试)

注意&#xff1a;从 Docker 19.03 开始&#xff0c;Docker 引入了对 NVIDIA GPU 的原生支持&#xff0c;因此若AI要调用GPU算力的话docker版本也是有要求的&#xff0c;后面博客测试。 当然本篇博客还没设计到GPU的调用&#xff0c;主要Pycharm加Anaconda的方案用习惯了&#…

基于Springboot的社区待就业人员信息管理系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的社区待就业人员信息管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三…

pdf加水印怎么加?自己原创的PDF资料分享到网络上需要采取一些版权保护的措施,添加水印就是个不错的选择

一&#xff0c;水印的基本概念 水印通常是一种用于标识文件来源、版权信息或防止非法复制的标记。它可以是文字、图形或图像等形式&#xff0c;以半透明或半淡化的方式嵌入到文件中&#xff0c;既不影响文件的正常阅读&#xff0c;又能起到标识和保护的作用。 二&#xff0c;…

mars3d实现禁止地图移动,禁止地图左右平移,但是鼠标可以移动的效果。

new mars3d.layer.GeoJsonLayer({渲染后实现鼠标左键按住不释放拖动时&#xff0c;地图不跟着拖动效果 当前问题&#xff1a; 1.在map初始化&#xff0c;或者是加载效果的时候&#xff0c;整个地球的场景都是一样的。 如果鼠标左键按住不释放&#xff0c;在屏幕上拖动的时候…

设计模式代码实战-责任链模式

1、问题描述 小明所在的公司请假需要在OA系统上发布申请&#xff0c;整个请求流程包括多个处理者&#xff0c;每个处理者负责处理不同范围的请假天数&#xff0c;如果一个处理者不能处理请求&#xff0c;就会将请求传递给下一个处理者&#xff0c;请你实现责任链模式&#xff…

C++:map和set的使用

一、关联式容器介绍 在学习map和set之前&#xff0c;我们接触到的容器有&#xff1a;vector、list、stack、queue、priority_queue、array&#xff0c;这些容器统称为序列式容器&#xff0c;因为其底层为线性序列的数据结构&#xff0c;里面存储的是元素本身。 关联式容器也是用…

Appian发布最新版本:通过AI流程自动化推动业务发展

Appian公司于2024年4月16日在弗吉尼亚州麦克莱恩宣布推出Appian平台的最新版本。此版本引入了Process HQ&#xff0c;这是一个集流程挖掘和企业AI于一体的系统&#xff0c;结合了Appian的数据平台。Process HQ为企业运营提供前所未有的可见性&#xff0c;支持数据驱动的决策和流…

微信小程序四(全局配置和页面配置页面跳转)

全局配置&#xff1a; 小程序根目录下的 app.json 文件用来对微信小程序进行全局配置&#xff0c;决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等 tabBar设置&#xff1a;最少两个最多5个 "tabBar": {"list":[{"pagePath": &qu…

【若依】代码生成详细教程(单表、主从表、树形表增删改查)

若依代码生成开发接口 修改代码生成配置一、单表实现增删改查1. 新建数据库表结构2. 新建模块&#xff0c;解决项目依赖3. 启动项目&#xff0c;新建菜单4. 导入数据表&#xff0c;自动生成代码5. 将生成代码粘贴到对应的模块&#xff0c;执行生成的sql&#xff08;用于生成菜单…

OpenHarmony网络协议通信—nanopb

简介 nanopb是一种小代码量的协议缓冲区实现&#xff0c;适用于任何内存受限的系统。 下载安装 直接在OpenHarmony-SIG仓中搜索nanopb并下载。 使用说明 以OpenHarmony 3.1 Beta的rk3568版本为例 将下载的Nanopb库代码存在以下路径&#xff1a;./third_party/nanopb 修改添…

一键设置个性手机壁纸:苹果手机怎么设置动态壁纸?

在苹果手机上设置动态壁纸是一种让你的手机屏幕更生动、更有趣的方式。无论是流动的水滴、绚丽的光影还是动态的星空&#xff0c;动态壁纸可以为你的手机带来全新的视觉体验。苹果手机怎么设置动态壁纸&#xff1f;在本文中&#xff0c;我们将介绍苹果手机上如何设置动态壁纸的…

李沐-16 PyTorch 神经网络基础【动手学深度学习v2】

注&#xff1a;1. 沐神对应章节视频出处 2.代码使用Jupyter Notebook运行更方便 3.文章笔记出处 一、层和块 层&#xff1a;层&#xff08;1&#xff09;接受一组输入&#xff0c; &#xff08;2&#xff09;生成相应的输出&#xff0c; &#xff08;3&#xff09;由一组可调整…

priority queue优先队列(三)

一、优先队列 优先队列不再遵循先进先出的原则&#xff0c;而是分为两种情况: 最大优先队列&#xff0c;无论入队顺序如何&#xff0c;都是当前最大的元素优先出队。 最小优先队列&#xff0c;无论入队顺序如何&#xff0c;都是当前最小的元素优先出队。 在操作系统中&#xf…

k8s 部署 kube-prometheus监控

一、Prometheus监控部署 1、下载部署文件 # 使用此链接下载后解压即可 wget https://github.com/prometheus-operator/kube-prometheus/archive/refs/heads/release-0.13.zip2、根据k8s集群版本获取不同的kube-prometheus版本部署 https://github.com/prometheus-operator/k…

达梦数据库一体机树立金融解决方案标杆

达梦数据库一体机自问世以来&#xff0c;获得众多行业用户的高度关注&#xff0c;并率先在金融行业吹响冲锋号角&#xff0c;实现多个重大项目的落地应用。近日&#xff0c;珠海华润银行股份有限公司基于达梦数据库一体机 I 系列的《数据库一体机银行多业务系统集中部署解决方案…

STM32之串口中断接收丢失数据

五六年没搞STM32了&#xff0c;这个项目一切都挺顺利&#xff0c;万万没想到被串口接收中断恶心到了。遇到的问题很奇怪 HAL_UART_Receive_IT(&huart1, &rx_buffer[rx_index], LCD_UART_LEN); 这个代码中 LCD_UART_LEN1的时候&#xff0c;接收过来的数据&#xff0c;数…

VR全景展览——开启全新视界的虚拟展览体验

随着VR技术的不断发展和成熟&#xff0c;VR全景展览已经成为现代展览行业的一大亮点。通过模拟现实世界的场景&#xff0c;VR全景展览为用户提供了一个沉浸式的观展体验&#xff0c;使参观者能够跨越地理和时间限制&#xff0c;探索不同领域的展览。 一、VR全景展览的功能优势 …
最新文章