【C++】模拟实现List的正向和反向迭代器(iterator、reverse_iterator)

news2024/11/17 20:29:47

文章目录

    • 1、搭建List的基本框架
    • 2、List中iterator和const_iterator
    • 3、反向迭代器revser_iterator

1、搭建List的基本框架

STL中List容器底层是一个双向带头循环链表。
在这里插入图片描述

这里简单搭建一个List,下面我们不断完善。
思路:
1、List作为一个双向带头链表,所以除了创建一个List类,还需要有一个结点结构体有着可以被外部访问的成员prev、next、data。
2、List应当有一个哨兵结点,所以默认构造必须我们写且初始化一个结点设为哨兵结点。

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

namespace test
{
	template<class T>
	struct Node
	{
		struct Node<T>* _prev;
		struct Node<T>* _next;
		T _data;
		
		//在后续插入建立新结点,初始化
		Node(const T& x = T())
			:_prev(nullptr)
			,_next(nullptr)
			,_data(x)
		{}
	};

	template<class T>
	class list
	{
		typedef struct Node<T> node;
	public:
		void initlist()
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;

			_size = 0;
		}

		// 构造
		list()
		{
			initlist();
		}
		

	private:
		node* _head;
		size_t _size;
	};


}

2、List中iterator和const_iterator

C++中的迭代器iterator虽然在使用上可以当成指针一样用,但是对于List如果要像指针一样,那么就需要对iterator的运算符进行重载实现。
那么可以将iterator封装成一个类进行实现。

有了迭代器,list就可以实现begin()和end()对链表结点进行定位。
并且也能在特定位置实现insert。

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

namespace test
{
	template<class T>
	struct Node
	{
		...
	};


	template<class T>
	class iterator
	{
		typedef struct Node<T> node;
	public:
		iterator(node* it)
			:_node(it)
		{}

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

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

		bool operator!=(iterator& it)
		{
			return it._node != _node;
		}

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

		node* getnode()
		{
			return _node;
		}

	private:
		node* _node;
	};


	template<class T>
	class list
	{
		typedef struct Node<T> node;
		typedef iterator<T> iterator;
	public:

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

		iterator end()
		{
			return iterator(_head); 
		}


		void initlist()
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;

			_size = 0;
		}

		// 构造
		list()
		{
			initlist();
		}

		//拷贝构造
		template <class InputIterator>
		list(InputIterator first, InputIterator last)
		{
			initlist();

			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

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

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

			_size++;

			return iterator(newnode);
		}

		void push_back(T x = T())
		{
			insert(end(), x);
		}
		
	private:
		node* _head;
		size_t _size;
	};
}

const_iterator

对于const对象,迭代器也需要用const的。
对于const迭代器,不能写成const iterator it 这修饰的是it,需要修饰的iterator所以需要一个新的类型const_iterator

const_iterator保证的是被指向的结点内容不能变化。
下面看两种情况
在这里插入图片描述

对于const和非const类型的改变,可以直接通过模板的类型泛型进行更改。

	// 同一个类模板实例化出的两个类型
	// typedef __list_iterator<T, T&, T*> iterator;
	// typedef __list_iterator<T, const T&, const T*> const_iterator;
	template<class T>
	class list_iterator
	{
		typedef struct Node<T> node;
	public:
		typedef list_iterator<T, Ref, Ptr> Self
		list_iterator(node* it)
			:_node(it)
		{}

		//前置
		Self& operator++()
		{
			_node = _node->_next;
			return *this;
		}

		//后置
		Self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}

		bool operator!=(list_iterator<T> x) const
		{
			return _node != x._node;
		}

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

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

		Ptr operator->()
		{
			return &(_pnode->data);
		}

		node* getnode()
		{
			return _node;
		}

	private:
		node* _node;
	};

编译器对it->_col这个位置做了一个优化,本来获取data地址后应该是it->->_col,但是编译器做了优化就是it->_col,但it.operator->()->_col这样不会受影响。

	struct Pos
	{
		int _row;
		int _col;

		Pos(int row = 0, int col = 0)
			:_row(row)
			,_col(col)
		{}
	};
	void Print_list(const list<Pos>& lt)
	{
		list<Pos>::const_iterator it = lt.begin();
		while (it != lt.end())
		{
			//我们知道it的底层
			//cout << (*it)._col << " " << (*it)._row << endl;
			//cout << (&(*it))->_row << ":" << (*it)._col << endl;
			//普通认为是指针
			cout << it->_col << " " << it->_row << endl;
			//cout << it.operator->()->_col << " " << it.operator->()->_row << endl;
			++it;
		}
		cout << endl;

	}

3、反向迭代器revser_iterator

反向迭代器就是从链表的尾部到头部。

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

在底层rend()其实就是begin(),rbegin()其实就是end(),
并且reverse_iterator其实是一个适配器,通过iterator封装的一个类,
所以如果实现了一个容器的正向迭代器,那么反向迭代器就一定能实现。

#include <iostream>
#include <assert.h>
using namespace std;

namespace test
{
	template <class Iterator, class Ref, class Ptr>
	class ReverseIterator
	{
		typedef ReverseIterator<Iterator, Ref, Ptr> self;
	public:
		ReverseIterator(Iterator it)
			:_it(it)
		{}

		Ref operator*()
		{
			Iterator tmp = _it;
			return *(--tmp);
		}

		Ptr operator->()
		{
			return &(operator*());
		}

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

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

		bool operator!=(const self& s) const
		{
			return _it != s._it;
		}
	private:
		Iterator _it;
	};


	template <class T>
	struct ListNode
	{
		struct ListNode<T>* _next;
		struct ListNode<T>* _prev;
		T data;

		ListNode(T x = T())
			:_next(nullptr)
			,_prev(nullptr)
			,data(x)
		{}
	};

	template <class T, class Ref, class Ptr>
	struct list_iterator
	{
		typedef struct ListNode<T> node;
		typedef struct list_iterator<T, Ref, Ptr> Self;

		list_iterator(node* p)
			:_pnode(p)
		{}

		bool operator!=(Self x) const
		{
			return _pnode != x._pnode;
		}

		bool operator==(Self x) const
		{
			return _pnode == x._pnode;
		}

		Ptr operator->()
		{
			return &(_pnode->data);
		}

		Ref operator*()
		{
			return _pnode->data;
		}

		Self& operator++() 
		{
			_pnode = _pnode->_next;
			return *this;
		}

		Self operator++(int)
		{
			Self tmp(*this);
			_pnode = _pnode->_next;
			return tmp;
		}

		Self& operator--() 
		{
			_pnode = _pnode->_prev;
			return *this;
		}

		Self operator--(int)
		{
			Self tmp(*this);
			_pnode = _pnode->_prev;
			return tmp;
		}

		node* _pnode;
	};


	template <class T>
	class list
	{
		typedef struct ListNode<T> node;
	public:
		//简化类型
		typedef list_iterator<T, T&, T*> iterator;
		typedef list_iterator<T, const T&, const T*> const_iterator;

		typedef ReverseIterator<iterator, T&, T*> reverse_iterator;
		typedef ReverseIterator<const_iterator, const T&, const T*> const_reverse_iterator;


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

		iterator end()
		{
			return iterator(_head);
		}

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

		const_iterator end() const
		{
			return const_iterator(_head);
		}

		reverse_iterator rbegin()
		{
			return end();
		}

		reverse_iterator rend()
		{
			return begin();
		}

		const_reverse_iterator rbegin() const
		{
			return end();
		}

		const_reverse_iterator rend() const
		{
			return begin();
		}

		void initlist()
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;

			_size = 0;
		}

		//构造
		list()
		{
			initlist();
		}

		//构造
		template <class InputIterator>
		list(InputIterator first, InputIterator last)
		{
			initlist();

			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

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

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

			list<T> tmp(lt.begin(), lt.end());
			swap(tmp);
		}

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

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

		size_t size() const
		{
			return _size;
		}
		
		~list()
		{
			clean();
			delete _head;
			_head = nullptr;
		}

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

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

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

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

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

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

			_size++;

			return iterator(newnode);
		}

		iterator erase(iterator pos)
		{
			assert(pos != end());

			node* cur = pos._pnode;
			node* prev = cur->_prev;
			node* next = cur->_next;
			delete cur;
			prev->_next = next;
			next->_prev = prev;

			_size--;

			return iterator(next);
		}

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

	private:
		node* _head;
		size_t _size;
	};

总结:
list迭代器为了重载其多个操作符,所以弄了一个封装类。迭代器在不同场景经过操作符函数返回了不同类型,其中还要区别const类型,通过模板泛型能方便我们应对这些情况。

反向迭代器是对正向迭代器的封装,所以实现了正向迭代器就能实现反向迭代器。

本节完~

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

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

相关文章

excel函数公式:常用高频公式应用总结 下篇

公式6&#xff1a;根据身份证号码计算出生日期要从身份证号码中得到出生日期&#xff0c;这种问题对于从事人资行政岗位的小伙伴一定不陌生&#xff0c;公式也比较简单&#xff1a;TEXT(MID(A2,7,8),"0-00-00")就能得到所需结果&#xff0c;如图所示&#xff1a;要明…

vue+xlsx实现表格的导入导出:

文章目录一、vue前端使用xlsx和 xlsx-style 导出excel&#xff0c;并修改样式:1、改造后效果&#xff1a;2、实现&#xff1a;3、引入库xlsx-style4、excelUtil.js文件二、前端xlsx插件怎么设置导出的excel列宽自适应&#xff1f;2-1、效果2-2、效果三、xlsx插件&#xff0c;导…

Java语法六:线程安全以及死锁

前言&#xff1a; 接下来我们要了解一下&#xff0c;线程安全的集合类有哪些&#xff1f;什么是死锁以及怎么避免死锁问题。 1.多线程环境使用哈希表 1.1&#xff1a;HashTable 只是简单的把关键方法加上synchronized关键字。 public synchronized V put(K key, V value) p…

(1分钟速览)最近邻搜索--激光SLAM常用必杀技

学习kdtree的数据结构&#xff0c;和二叉树的思想大体上算是比较相似的。参考博客&#xff1a;https://blog.csdn.net/dive_shallow/article/details/113136145建树&#xff0c;一个递归的操作。这里的删除操作很好理解&#xff0c;分左子树和右子树两种情况。如果是左子树那么…

Go语言设计与实现 -- 内存对齐机制

什么是内存对齐 为了能让CPU可以更快的存取到各个字段&#xff0c;Go编译器会帮你把struct结构体做数据的对齐。所谓的数据对齐&#xff0c;是指内存地址是所存储数据的大小&#xff08;按字节为单位&#xff09;的整数倍&#xff0c;以便CU可以一次将该数据从内存中读取出来。…

每天五分钟机器学习:推荐系统中所有用户的损失函数是什么?

本文重点 我们分析机器学习算法都是从损失函数的角度来说的,为了找到最佳的参数θ,可以最小化损失函数,那么本节课程我们将学习基于内容的推荐系统的损失函数是什么? 数据集 我们将每一个电影称为样本,每个电影有两个特征x1、x2,其中x1表示该电影为恐怖片的程度,x2表…

测试开发 | 跨平台设备管理方案 Selenium Grid

Selenium Grid 是 Selenium 的三大组件之一&#xff0c;它可以在多台机器上并行运行测试&#xff0c;集中管理不同的浏览器版本和浏览器配置。通过将客户端命令发送到远程浏览器的实例, Selenium Grid 允许在远程计算机 (虚拟或真实) 上执行 WebDriver 脚本. 它旨在提供一种在多…

SSL证书的类型

SSL证书类型分3类&#xff1a;DV OV EV.域名型&#xff08;DV&#xff09;SSL证书: 信任等级一般&#xff0c;只需验证网站的真实性便可颁发证书保护网站。企业型&#xff08;OV&#xff09;SSL证书: 信任等级强&#xff0c;须要验证企业的身份&#xff0c;审核严格&#xff0c…

VS2017基于CLR运行环境的VC++.NET界面编程(数据库为mysql)

此篇献给暨南大学珠海校区还在用VS2013编写VC++.NET界面程序的苦逼们! 文章分为四个部分: 1、运行环境的搭建 2、数据库的连接 3、控件介绍 4、程序编写示例 程序效果演示视频和程序源码及数据库文件附于文末。 一、运行环境的搭建 VS自从2013版本后淘汰VC++.NET,编写界面…

恒星物联河道流量监测站——雷达流量计

一、产品概述 河道流量监测站是一款采用一体化设计的河道流量智能监测设备&#xff0c;设备由河道雷达流量计、遥测终端机、太阳能供电系统组成。雷达流量计采用先进的K波段平面雷达技术&#xff0c;通过非接触的方式测量水体的流速和水位&#xff0c;根据内置的软件算法&…

TCP协议重点总结(万字总结-附实例)

文章目录前言一、网络的原生情况二、TCP协议2.1 TCP的特点2.2 TCP协议段格式2.3 TCP原理2.3.1 确认应答机制&#xff08;可靠机制&#xff09;2.3.2 序列号2.3.3 超时重传机制&#xff08;可靠机制&#xff09;2.3.4 连接管理机制&#xff08;可靠机制&#xff09;2.3.5 滑动窗…

RabbitMQ的简介和简单使用

同步调用异步调用MQRabbitMQ的使用docker拉取docker pull rabbitmq:3-management启动容器docker run \-e RABBITMQ_DEFAULT_USERitcast \ &#xff08;账号&#xff09;-e RABBITMQ_DEFAULT_PASS123321 \ &#xff08;密码&#xff09;--name mq \--hostname mq1 \-p 15672:156…

开发工具中SpringBoot使用外置Tomcat启动 (亲测有效)-第458篇

历史文章&#xff08;文章累计450&#xff09; 《国内最全的Spring Boot系列之一》 《国内最全的Spring Boot系列之二》 《国内最全的Spring Boot系列之三》 《国内最全的Spring Boot系列之四》 《国内最全的Spring Boot系列之五》 SpringBoot添加外部jar包及打包(亲测有…

评测5款国内外免费远控,谁是最好用第一名?

远程控制应用不少人都有了解使用过&#xff0c;尤其是会常用电脑进行工作的群体&#xff0c;比如程序员、设计师、运维、文员等岗位。在隔离居家远程办公时&#xff0c;通过家里的手机、平板或电脑跨系统、跨设备操控公司所用的办公电脑&#xff0c;就能及时处理工作内容&#…

前端使用lottie-web,使用AE到处的JSON动画贴心教程

Lottie简介 官方介绍&#xff1a;Lottie是一个库&#xff0c;可以解析使用AE制作的动画&#xff08;需要用bodymovie导出为json格式&#xff09;,支持web、ios、android、flutter和react native。 在web端&#xff0c;lottie-web库可以解析导出的动画json文件&#xff0c;并将其…

02_FreeRTOS移植

目录 获取FreeRTOS源码 FreeRTOS源码内容 FreeRTOS内核 Demo文件夹 Source文件夹 portable文件夹 FreeRTOS移植 移植步骤 移植详解 实验源码: 获取FreeRTOS源码 FreeRTOS官网:https://www.freertos.org/ FreeRTOS源码内容 FreeRTOS内核 Demo文件夹 Demo文件夹里面就…

《Protein Actions Principles and Modeling》-《蛋白质作用原理和建模》中文分享(15)

《Protein Actions Principles and Modeling》-《蛋白质作用原理和建模》 本人能力有限&#xff0c;如果错误欢迎批评指正。 第四章&#xff1a;Protein Binding Leads to Biological Actions &#xff08;蛋白质的结合会产生生物作用&#xff09; 如果我们想要对一个结合过…

Java 泛型中的通配符详解

目录 1、如何定义和使用上界通配符&#xff1f; 2、如何定义和使用无界通配符&#xff1f; 3、如何定义和使用下界通配符&#xff1f; 4、如何使用通配符定义泛型类或接口之间的子类型关系&#xff1f; 5、通配符的捕获和辅助方法 6、通配符使用指南 在泛型代码中&#x…

C++ txt文本文件处理系统(c++学习小例子)

C++ txt文本文件处理系统(c++学习小例子) 一、界面示例二、 要求2.1 数据格式2.2 实现功能三、代码3.1 classfi.h3.2 classfi.cpp3.3 main.cpp四、 使用说明一、界面示例 二、 要求 2.1 数据格式 现有DEM数据,其格式为DEM_data.txt,可在文章末尾下载。文本存储格式如下: …

用javascript分类刷leetcode19.数组(图文视频讲解)

数组操作的时间复杂度 Access&#xff1a;O(1) Search&#xff1a;O(n) Insert&#xff1a; 平均O(n)&#xff0c;最好的情况下O(1)&#xff0c;也就是在数组尾部插入O(1)&#xff0c;最坏的情况下O(n) Delete&#xff1b;平均O(n)&#xff0c;最好的情况下O(1)&#xff0c;…