STL list容器底层代码剖析、正向迭代器与反向迭代器的类的封装

news2025/1/17 15:15:12

目录

一、迭代器是什么

二、迭代器的本质

三、如何给一个容器实现迭代器功能

 四、正向迭代器功能的具体实现

 五、反向迭代器

 六、list底层代码剖析


一、迭代器是什么

迭代器(iterator)有时又称光标(cursor)是程序设计的软件设计模式,可在容器对(container,例如链表或数组)上遍访的接口,设计人员无需关心容器对象的内存分配的实现细节。

各种语言实现迭代器的方式皆不尽同,有些面向对象语言像Java,C#,Ruby,Python,Delphi都已将迭代器的特性内置语言当中,完美的跟语言集成,我们称之隐式迭代器(implicit iterator),但像是C++语言本身就没有迭代器的特色,但STL仍利用模板实现了功能强大的迭代器。STL容器的数据的内存地址可能会重新分配(reallocate),与容器绑定的迭代器仍然可以定位到重新分配后的正确的内存地址。

二、迭代器的本质

迭代器的本质其实就是指针 (vector、string) ,或者是对指针的进一步封装(list)

三、如何给一个容器实现迭代器功能

① 对原生态指针进行封装,实现一个迭代器的类

② 在容器中给迭代器类型取别名 iterator,为了方便用户操作

③ 容器中增加begin() 、end()接口

④ 迭代器类中实现解引用 *  -> 元素访问功能

⑤ 迭代器类中实现迭代器移动功能 ++(以及--)

⑥ 迭代器类中实现比较功能 == 和 !=

 四、正向迭代器功能的具体实现

① T Ptr Ref 分别为迭代器中对应不同类型的模板参数 对应这解引用*与->俩个下标运算法不同的操作返回值类型

② self (本身) 是作为ListIterator<T, Ptr, Ref> 的一个别名,方便于函数的返回,如下,二者是完全一致的

    template<class T, class Ptr, class Ref>
	class ListIterator
	{
		typedef ListNode<T> Node;
		typedef ListIterator<T, Ptr, Ref> self;
	public:
		ListIterator(Node* pNode = nullptr)
			: _pNode(pNode)
		{}
		/
		/// 解引用
		Ref operator*()
		{
			return _pNode->val;
		}
		Ptr operator->()
		{
			return &(_pNode->val);
		}
		/
		/// ++ -- 
		self& operator++()
		{
			_pNode = _pNode->next;
			return* this;
		}
		self operator++(int)
		{
			self temp(*this);
			_pNode = _pNode->next;
			return temp;	// 由于temp为该函数体内部变量,所以返回时候只能按照值进行返回
		}
		self& operator--()
		{
			_pNode = _pNode->prev;
			return* this;
		}
		self operator--(int)
		{
			self temp(*this);
			_pNode = _pNode->prev;
			return temp;	// 由于temp为该函数体内部变量,所以返回时候只能按照值进行返回
		}
		/
		/// 实现比较
		bool operator==(const self& s)const
		{
			return _pNode == s._pNode;
		}
		bool operator!=(const self& s)const
		{
			return _pNode != s._pNode;
		}
		Node* _pNode;
	};

 list容器中的begin()、end()接口、以及迭代器的更名:

 五、反向迭代器

由于反向迭代器的功能与正向迭代器几乎相似,有少部分的功能相反操作,所以在实现上直接以正向迭代器为模板,进行调用使用。

里面有一些注意点:

①    对于反向迭代器来说前置  rit++ 就相当于 正向迭代器 it--

        对于俩个迭代器来++--符号位置不变正负号相反

 ② 注意解引用的位置

        正向解引用直接把当前结点的值以引用的方式返回即可

        而反向迭代器解引用需要需要进行一步后移操作

        比如:进行rbgin()到rend()的打印,如果直接从rbegin()位置开始打印,就会直接打印根节点

③ 对于Ptr 和 Ref 因为是用正向迭代器作为模板,所以如果要使用就要到正向迭代器中去找

        使用typedef Iterator::Ptr进行访问正向迭代器中的Ptr

④ 为了方便使用正向迭代器中的Iterator::Ptr ,给它取个别名Ptr

        但是直接使用typedef进行取别名 编译器发生报错

        因为静态成员变量在类外访问,也是通过类名+ : : (作用域限定符) +静态成员变量名来进行访

        问,只有加上typename后,编译器才知道这里的typedef是给一个类型取别名,而不是操作

        静态成员函数

template<class Iterator>
	class Reverse_Iterator
	{
		typename typedef Iterator::Ptr Ptr;
		typename typedef Iterator::Ref Ref;
		typedef Reverse_Iterator<Iterator> self;
	public:
		Reverse_Iterator(Iterator it)
			: _it(it)
		{}
		/
		/// 解引用
		Ptr operator*()
		{
			// return *_it; 不可以。因为对于反向迭代器来说从rbegin()位置开始,
			//				rbegin()为头结点位置,所以解引用应该
            //              返回rbegin()的下一个结点即最后一个元素
			Iterator temp(*this);
			temp--;
			return temp;
		}
		Ptr operator->()
		{
			return &(_it->pNode->_val);
		}
		/
		/// ++ --
		self& operator++()
		{
			--_it;
			return *this;
		}
		self operator++(int)
		{
			_it--;
			return *this;
		}
		self& operator--()
		{
			++_it;
			return *this;
		}
		self operator--(int)
		{
			_it++;
			return *this;
		}
		/
		/// 比较
		bool operator==(const self& s)const
		{
			return _it == s._it;
		}
		bool operator!=(const self& s)const
		{
			return _it != s._it;
		}
		Iterator _it;
	};

 六、list底层代码剖析

#include<iostream>
using namespace std;

namespace weige
{
	template<class T>
	struct ListNode
	{
		ListNode(const T& value = T())
			: next(nullptr)
			, prev(nullptr)
			, val(value)
		{}
		ListNode* next;
		ListNode* prev;
		T val;
	};

	template<class T, class Ptr, class Ref>
	class ListIterator
	{
		typedef ListNode<T> Node;
		typedef ListIterator<T, Ptr, Ref> self;
	public:
		// 主要是给后序反向迭代器使用
		typedef Ptr Ptr;
		typedef Ref Ref;
	public:
		ListIterator(Node* pNode = nullptr)
			: _pNode(pNode)
		{}
		/
		/// 解引用
		Ref operator*()
		{
			return _pNode->val;
		}
		Ptr operator->()
		{
			return &(_pNode->val);
		}
		/
		/// ++ -- 
		/*ListIterator<T, Ptr, Ref>& operator++()
		{
			_pNode = _pNode->next;
			return*this;
		}*/
		self& operator++()
		{
			_pNode = _pNode->next;
			return* this;
		}
		self operator++(int)
		{
			self temp(*this);
			_pNode = _pNode->next;
			return temp;	// 由于temp为该函数体内部变量,所以返回时候只能按照值进行返回
		}
		self& operator--()
		{
			_pNode = _pNode->prev;
			return* this;
		}
		self operator--(int)
		{
			self temp(*this);
			_pNode = _pNode->prev;
			return temp;	// 由于temp为该函数体内部变量,所以返回时候只能按照值进行返回
		}
		/
		/// 实现比较
		bool operator==(const self& s)const
		{
			return _pNode == s._pNode;
		}
		bool operator!=(const self& s)const
		{
			return _pNode != s._pNode;
		}
		Node* _pNode;
	};

	template<class Iterator>
	class Reverse_Iterator
	{
		typename typedef Iterator::Ptr Ptr;
		typename typedef Iterator::Ref Ref;
		typedef Reverse_Iterator<Iterator> self;
	public:
		Reverse_Iterator(Iterator it)
			: _it(it)
		{}
		/
		/// 解引用
		Ref operator*()
		{
			// return *_it; 不可以因为对于反向迭代器来说从rbegin()位置开始,
			//				rbegin()为头结点位置,所以解引用应该返回rbegin()的下一个结点即最后一个元素
			Iterator temp(_it);
			--temp;
			return *temp;
		}
		Ptr operator->()
		{
			return &(_it->pNode->_val);
		}
		/
		/// ++ --
		self& operator++()
		{
			--_it;
			return *this;
		}
		self operator++(int)
		{
			_it--;
			return *this;
		}
		self& operator--()
		{
			++_it;
			return *this;
		}
		self operator--(int)
		{
			_it++;
			return *this;
		}
		/
		/// 比较
		bool operator==(const self& s)const
		{
			return _it == s._it;
		}
		bool operator!=(const self& s)const
		{
			return _it != s._it;
		}
		Iterator _it;
	};

	template<class T>
	class list
	{
	public:
		typedef ListNode<T> Node;

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

		typedef Reverse_Iterator<iterator> reverse_iterator;
		typedef Reverse_Iterator<const_iterator> const_reverse_iterator;
		
		/// 构造函数
		list()
		{
			CreateHeadNode();
		}
		// 使用参数为int类型的n个值为value的元素进行构造
		list(int n, const T& value = T())
		{
			CreateHeadNode();
			for (int i = 0; i < n; ++i)
				push_back(value);
		}

		template <class Iterator>
		list(Iterator first, Iterator last)
		{
			CreateHeadNode();
			while (first != last)
			{
				push_back(*first);
				first++;
			}
		}
		list(list& L)
		{
			CreateHeadNode();
			for (auto e : L)
			{
				push_back(e);
			}
		}
		list& operator=(const list L)
		{
			this->swap(L);
			return *this;
		}
		
		/ 正向迭代器
		iterator begin()
		{
			return iterator(_head->next);
		}
		iterator end()
		{
			return iterator(_head);
		}

		const_iterator begin()const
		{
			return iterator(_head->next);
		}
		const_iterator end()const
		{
			return iterator(_head);
		}
		
		/ 反向迭代器
		reverse_iterator rbegin()
		{
			return reverse_iterator(end());
		}
		reverse_iterator rend()
		{
			return reverse_iterator(begin());
		}

		const_reverse_iterator rbegin()const
		{
			return reverse_iterator(end());
		}
		const_reverse_iterator rend()const
		{
			return reverse_iterator(begin());
		}
		
		/ 元素访问
		T& front()
		{
			return _head->next->val;
		}
		const T& front()const
		{
			return _head->next->val;
		}
		T& back()
		{
			return _head->prev->val;
		}
		const T& back()const
		{
			return _head->prev->val;
		}
		
		/ 容量
		bool empty()const
		{
			return _head == _head->next;
		}
		size_t size()const
		{
			Node* cur = _head->next;
			size_t count = 0;
			while (cur != _head)
			{
				count++;
				cur = cur->next;
			}
			return count;
		}
		void resize(size_t newsize, const T& val = T())
		{
			size_t oldsize = size();
			if (newsize < oldsize)
				for (size_t i = newsize; i < oldsize; ++i)
					pop_back();
			else
				for (size_t i = oldsize; i < newsize; ++i)
					push_back(val);
		}
		
		/ 元素修改
		void push_back(const T& val = T())
		{
			insert(end(), val);
		}
		void pop_back()
		{
			auto pos = end();
			pos--;
			erase(pos);
		}
		void push_front(const T& val = T())
		{
			insert(begin(), val);
		}
		void pop_front()
		{
			erase(begin());
		}
		iterator insert(iterator Itpos, const T& val = T())
		{
			Node* pos = Itpos._pNode;
			Node* newNode = new Node(val);
			newNode->next = pos;
			newNode->prev = pos->prev;
			newNode->prev->next = newNode;
			pos->prev = newNode;
			return pos->prev;
		}
		iterator erase(iterator Itpos)
		{
			Node* pos = Itpos._pNode;
			if (pos == _head)
				return pos;
			pos->next->prev = pos->prev;
			pos->prev->next = pos->next;
			Node* ret = pos->next;
			delete pos;
			return ret;
		}
		void clear()
		{
			Node* cur = _head->next;
			// 采用头删法
			while (cur != _head)
			{
				_head->next = cur->next;
				delete cur;
				cur = _head->next;
			}
			_head->next = _head;
			_head->prev = _head;
		}
	private:
		void CreateHeadNode()
		{
			_head = new Node();
			_head->next = _head;
			_head->prev = _head;
		}
	private:
		Node* _head;
	};
}

void TestMylist1()
{
	weige::list<int> L1;
	weige::list<int> L2(10, 5);

	int array[] = { 0,1,2,3,4,5,6,7 };
	weige::list<int> L3(array, array + sizeof(array) / sizeof(array[0]));
	weige::list<int> L4(L3);
}

void TestMylist2()
{
	weige::list<int> L1;
	L1.push_back(1);
	L1.push_back(2);
	L1.push_back(3);
	L1.push_back(4);
	L1.push_back(5);
	L1.push_back(6);
	L1.push_back(7);
	L1.push_back(8);
	L1.push_back(9);
	cout << L1.size() << endl;
	cout << L1.back() << endl;
	cout << L1.front() << endl;
	L1.erase(L1.begin());
	// L1.erase(L1.begin(), L1.end());
	cout << L1.size() << endl;
	cout << L1.back() << endl;
	cout << L1.front() << endl;
	L1.clear();
	cout << L1.size() << endl;
	cout << L1.back() << endl;
	cout << L1.front() << endl;
}

void TestMylist3()
{
	weige::list<int> L1;
	L1.push_back(1);
	L1.push_back(2);
	L1.push_back(3);
	L1.push_back(4);
	L1.push_back(5);
	L1.push_back(6);
	L1.push_back(7);
	L1.push_back(8);
	L1.push_back(9);
	L1.resize(10, 10);
	cout << L1.size() << endl;
	cout << L1.back() << endl;
	cout << L1.front() << endl;
	L1.resize(5);
	cout << L1.size() << endl;
	cout << L1.back() << endl;
	cout << L1.front() << endl;
}

void PrintList()
{
	weige::list<int> L1;
	L1.push_back(1);
	L1.push_back(2);
	L1.push_back(3);
	L1.push_back(4);
	L1.push_back(5);
	L1.push_back(6);
	L1.push_back(7);
	L1.push_back(8);
	L1.push_back(9);
	L1.resize(10, 10);
	auto it = L1.begin();
	while (it != L1.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
}

void TestMyList4()
{
	int array[] = { 0,1,2,3,4,5,6,7 };
	weige::list<int> L(array, array + sizeof(array) / sizeof(array[0]));
	auto it = L.begin();
	while (it != L.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	auto rit = L.rbegin();
	while (rit != L.rend())
	{
		cout << *rit << " ";
		rit++;
	}
	cout << endl;
}

int main()
{
	// TestMylist1();
	// TestMylist2();
	// TestMylist3();
	// PrintList();
	TestMyList4();
	return 0;
}

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

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

相关文章

【手写 Vue2.x 源码】第六篇 - 数据代理的实现

一&#xff0c;前言 上篇&#xff0c;主要介绍了 Vue 数据初始化流程中&#xff0c;数组类型的数据劫持是如何实现的&#xff0c;核心思路如下&#xff1a; 出于对性能的考虑&#xff0c;Vue 没有对数组采用 Object.defineProperty 进行递归劫持&#xff0c;而是对能够导致原…

NOSQL数据库习题

NOSQL数据库习题第一章第二章第三章第四章第五章NoSQL数据库上机测试第一章 1.写出DB、RDB、DBMS、TRDB、NoSQL、NewSQL、NDFS的中文名称。 答&#xff1a;DB&#xff1a;数据库 RDB&#xff1a;关系型数据库 DBMS&#xff1a;数据库管理系统 TRDB&#xff1a;传统关系型数据…

解决Shotgrid登陆不上, 上传出错,窗口卡住不动等问题

在使用Shotgrid时&#xff0c;是否遇到这种问题&#xff1a; accounts.autodesk.com出错 auth.autodesk.com出错 再或者这样出错&#xff1a; Shotgrid登陆出错 再再这样关闭Load…时这样出错&#xff0c; 窗口半天都关闭不了&#xff1a; 分析&#xff1a; 1 出现这种错…

(一分钟)激光SLAM扫描匹配 文献阅读

本博客介绍了激光SLAM扫描匹配的相关问题。编辑切换为居中添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09;扫描匹配也是数据关联类的问题&#xff0c;是SLAM里面的重要构成部分。编辑切换为居中添加图片注释&#xff0c;不超过 140 字&#xff08;可选&…

PyTorch实例2——文本情绪分类器

实例主要用于熟悉相关模型&#xff0c;并且练习创建一个模型的步骤&#xff1a;数据收集、数据预处理、构建模型、训练模型、测试模型、观察模型表现、保存模型传送门&#xff1a;蓝桥云课实验 目录1. 实验环境2. 实验目的3. 相关原理4. 实验步骤4.1 数据收集从在线商城抓取评论…

OpenHarmony 标准系统HDF框架之I2C驱动开发

OpenHarmony 标准系统HDF框架之I2C驱动开发主要内容I2C 基础知识## I2C 基础知识 —— 概念和特性I2C 基础知识 —— 协议、四种信号组合I2C 调试手段## I2C 调试手段 —— 硬件I2C 调试手段 —— 软件HDF 框架下的 I2C 设备驱动## HDF 框架下的 I2C 设备驱动 —— 案例描述HDF…

Shell 传递参数

我们可以在执行 Shell 脚本时&#xff0c;向脚本传递参数&#xff0c;脚本内获取参数的格式为&#xff1a;$n。n 代表一个数字&#xff0c;1 为执行脚本的第一个参数&#xff0c;2 为执行脚本的第二个参数&#xff0c;以此类推……实例以下实例我们向脚本传递三个参数&#xff…

ArcGIS Pro脚本工具(15)——按字段属性值分类导图

之前做了配合地图系列批量导图的脚本工具 ArcGIS Pro脚本工具&#xff08;9&#xff09;——配合地图系列批量导图_学学GIS的博客-CSDN博客_arcgispro批量导出地图pngPro的地图系列是批量制图的利器&#xff0c;但是有个不便的地方&#xff0c;就是设置完成地图系列后&#xf…

机器学习实战教程(13篇)

机器学习实战教程(13篇)这些网址非常适合想学习机器学习&#xff0c;却苦于没有项目&#xff08;尤其缺少数据&#xff09;的人。无意中看到&#xff0c;给自己做一个记录。机器学习实战教程&#xff08;一&#xff09;&#xff1a;K-近邻算法&#xff08;史诗级干货长文&#…

2022年度外设产品行业增长分析:鼠标、键盘同比增长27%

近年来&#xff0c;信息技术迅速发展&#xff0c;网民数量不断增长&#xff0c;深刻改变着社会的运作方式&#xff0c;对社会生产、商业运作模式等方面产生重大影响。广泛的行业应用为网络设备品牌商、制造商带来了差异化的细分市场&#xff0c;各类互联网设备接入数量也快速增…

混合空间增强

混合图像增强是一种图像处理技术&#xff0c;用于在不损失图像细节的情况下增强图像的对比度和亮度。它通常通过将图像拆分成多个通道&#xff0c;然后对每个通道进行独立处理来实现。 综合利用平滑滤波&#xff0c;锐化滤波&#xff0c;灰度拉伸等技术对图像进行处理&#xff…

【Linux】sudo给某条指令提权

sudo1.为什么要有sudo2.使用sudo提权的前提2. 在root下添加普通用户到信任列表3.验证4.总结1.为什么要有sudo 首先我们要知道&#xff0c;普通用户是受Linux的权限约束的。就比如普通用户对自己的家目录有完全管理的权限&#xff0c;但是不能随意查看其他人的家目录&#xff0c…

1.机器学习中的关键组件

1.机器学习中的关键组件 无论什么类型的机器学习问题&#xff0c;都会遇到这些组件&#xff1a; 可以用来学习的_数据_&#xff08;data&#xff09;&#xff1b;如何转换数据的_模型_&#xff08;model&#xff09;&#xff1b;一个_目标函数_&#xff08;objective function…

JavaScript 输出

文章目录JavaScript 输出JavaScript 显示数据使用 window.alert()操作 HTML 元素在本教程中写到 HTML 文档写到控制台您知道吗?JavaScript 输出 JavaScript 没有任何打印或者输出的函数。 JavaScript 显示数据 JavaScript 可以通过不同的方式来输出数据&#xff1a; 使用 wi…

TiDB 6.5 LTS 发版

在 2023 伊始&#xff0c;我们很高兴向大家宣布&#xff0c;TiDB 6.5 LTS 版本已经发布了。这是 TiDB V6 的第二个长期支持版&#xff08;上一个是 TiDB 6.1&#xff09;&#xff0c;除了携带了诸多备受期待的新特性&#xff0c;同时也将得到 TiDB 开发社区的长期维护&#xff…

设计模式(一):DDD领域驱动设计

DDD重架构&#xff0c;轻业务&#xff01; – 从单体架构开始快速验证DDD设计模式 贫血模型充血模型防腐层 DDD四层架构规范DDD单体架构和微服务架构隔离 本地单体SPI 插拔式扩展业务模式Nacos 插拔式扩展业务模式 ---- 服务名作为服务service code DDD清晰架构 – 构建领域仓库…

加解密与HTTPS(5)

您好&#xff0c;我是湘王&#xff0c;这是我的CSDN博客&#xff0c;欢迎您来&#xff0c;欢迎您再来&#xff5e;咱们大学读完之后有毕业证书&#xff0c;并且这个证书可以在学信网查询。专业上有注会、CCIE、律师证等&#xff0c;可以在国家职业认证机构或委托机构的网站上查…

【UCIe】UCIe DLP/DLLP 介绍

&#x1f525;点击查看精选 UCIe 系列文章&#x1f525; &#x1f525;点击进入【芯片设计验证】社区&#xff0c;查看更多精彩内容&#x1f525; &#x1f4e2; 声明&#xff1a; &#x1f96d; 作者主页&#xff1a;【MangoPapa的CSDN主页】。⚠️ 本文首发于CSDN&#xff0…

DNS协议——域名解析

DNS协议 DNS协议是一个应用层协议&#xff0c;作用是域名解析   使用DNS协议的原因&#xff0c;就是因为哪怕是点分十进制&#xff0c;对于我们都不太友好&#xff0c;因此使用域名来代替IP地址&#xff0c;比如www.baidu.com   最开始的时候&#xff0c;DNS系统只是一个普…

java基于ssm的个人博客系统个人博客网站个人博客项目源码

简介 Java ssm开发的个人博客系统&#xff0c;可以发布博客&#xff0c;照片&#xff0c;站长留言。 演示视频 https://www.bilibili.com/video/BV1sf4y1y7Ne/?share_sourcecopy_web&vd_sourceed0f04fbb713154db5cc611225d92156 技术 mysql,SSM,css,js,jq 功能 简单…