[C++]:12:模拟实现list

news2024/10/2 14:24:40

[C++]:12:模拟实现list

  • 一.看一看SGI的stl_list的源码:
    • 1.基础结构+构造函数
      • 1.节点结构:
      • 2.节点构造函数:
      • 3.链表结构:
      • 4.链表的构造函数:
    • 2.析构
      • 1.节点析构:
      • 2.链表的析构:
    • 3.迭代器
  • 二.模拟实现list
    • 1.基础结构+构造函数:
      • 1.节点:
      • 2.链表:
      • 3.实现迭代器+遍历数据:
        • 1.迭代器实现:
        • 3.数据遍历(可读可写)
        • 4.数据遍历(只读)
      • 3.拷贝构造+赋值
    • 2.增
      • 1.insert
      • 2.push_front && push_back
    • 3.删
      • 1.erase
      • 2.pop_front && pop_back
    • 4.查
      • 1.find
    • 5.改:
      • 2.amend
    • 6.析构函数:
      • 1.clear 和 ~list
    • 7.容量相关的函数:
      • 1.size()
      • 2.empty()
    • 8.实现operator->的意义:
      • 1.简单解决问题的方法:
      • 2.实现operator->
      • 3.一个问题:

一.看一看SGI的stl_list的源码:

1.基础结构+构造函数

1.节点结构:

在这里插入图片描述

1.SGI下的节点通过两个结构体实现。
2.基础的链表节点只包括前驱指针和后继指针。
3.链表节点去继承基础链表节点,新增节点数据。
4.优化掉指针类型带模板参数。

2.节点构造函数:

1.节点本身在这个地方是没有构造函数的。
2.观察下面的链表的结构和链表的构造函数可以观察出一点细节。

3.链表结构:

1._last_base类的构造通过_M_get_node方法进行节点的空间分配。
2.初始化一个基础的链表需要构造一个哨兵位的头节点。
3.开始的时候让哨兵位的头节点自己指向自己构造一个双向带头循环的一个结构。
4.list类继承_list_base类的时候里面多了许多的typedef

在这里插入图片描述
请添加图片描述

4.链表的构造函数:

1.通过上面的代码我们发现我们构造一个节点并没有通过节点的构造函数进行构造。
2.在list类型中提供一个方法去在插入新的节点的时候去调用。

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

2.析构

1.节点析构:

在这里插入图片描述

1.使用了内存池去回收节点的空间。

2.链表的析构:

在这里插入图片描述

3.迭代器

1.类比string或者vector他们的迭代器就是原生指针是比较方便的。
2.重写operator++ 和 operator*
3.对于节点来说结构不同于string和vector的。
4.参考SGI的源码发现对于内置类型是不可以实现运算符的重载。
5.实现一个迭代器的类型!

在这里插入图片描述

在这里插入图片描述

二.模拟实现list

1.基础结构+构造函数:

1.节点:

1.自己模拟实现就不这么复杂。
2.sgi通过内存池获取空间通过_creat_node get_node函数去对新增节点的创建。
3.节点自己把自己的数据在内部调整好的构造函数。
4.insert这样的函数去处理定义节点的问题。

//1.节点结构
	template<class T>
	struct ListNode {
		//1.节点的构造函数:
		ListNode(T x)
		{
			date = x;
		}
		//1.类+模板-->具体的类型。
		ListNode<T>* prev=nullptr;
		ListNode<T>* next=nullptr;
		T date;
	};

2.链表:

//3.链表结构
	template<class T>
	class list{
	public:
		//1.构造:双向带头循环链表的基本结构!
		list()
			:head(new ListNode<T>(T()))
		{
			head->prev = head;
			head->next = head;
		}
	private:
		ListNode<T>* head;
	};
}

3.实现迭代器+遍历数据:

1.内置类型没有办法进行运算符的重载。
2.迭代器本质就是节点的指针。
3.把一个节点指针类型包裹在一个自定义类型中。
4.在list和iterator_ListNode类中都对相应的类型进行了重定义。

1.迭代器实现:
template<class T>
	struct itreator_ListNode {
		//2.提供迭代器的方法:
		typedef itreator_ListNode<T> self;
		typedef ListNode<T> Node;
		Node* _node;

		itreator_ListNode(Node* x)
			:_node(x)
		{}

		//1.运算符重载:
		bool operator!=(self x)
		{
			return this->_node != x._node;
		}
		bool operator==(self x)
		{
			return this->_node == x._node;
		}
		//2.运算符重载++ --
		self& operator++()
		{
			this->_node = this->_node->next;
			return *this;
		}
		self operator++(int)
		{
			self ret = *this;
			this->_node = this->_node->next;
			return ret;
		}
		self& operator--()
		{
			this->_node = this->_node->prev;
			return *this;
		}
		self operator--(int)
		{
			self ret = *this;
			this->_node = this->_node->prev;
			return ret;
		}

		T& operator*()
		{
			return this->_node->date;
		}
	};
	template<class T>
	class list{
	public:
		//1.构造:双向带头循环链表的基本结构!
		list()
			:head(new ListNode<T>(T()))
		{
			head->prev = head;
			head->next = head;
		}

		//2.提供迭代器的方法:
		typedef itreator_ListNode<T> iterator;
		typedef ListNode<T> Node;

		//2-1:迭代器应该满足左闭右开

		//List_Node<T>* 类型到iterator类型是通过单参数的构造函数支持的!
		iterator begin() { return head->next; }
		iterator end() {return head;}

		
		void push_back(T x)
		{
			//1.产生一个节点:
			ListNode<T>* newnode = new ListNode<T>(x);
			//2.进行节点的连接:
			ListNode<T>* prev = head->prev;
			prev->next = newnode;
			newnode->prev = prev;
			head->prev = newnode;
			newnode->next = head;
		}


	private:
		Node* head;
	};
3.数据遍历(可读可写)

在这里插入图片描述

4.数据遍历(只读)

1.提供const迭代器。
2.注意:const迭代器并不是迭代器本身是const类型,如果迭代器本身是const类型不能实现operator++ 或者operator–。
3.有两个方法去实现const迭代器!

方法一:定义一个新的类:

//提供一个新的类
	template<class T>
	struct iterator_const_ListNode {
		//2.提供迭代器的方法:
		typedef iterator_const_ListNode<T> self;
		typedef ListNode<T> Node;
		Node* _node;

		iterator_const_ListNode(Node* x)
			:_node(x)
		{}

		//1.运算符重载:
		bool operator!=(self x)
		{
			return this->_node != x._node;
		}
		bool operator==(self x)
		{
			return this->_node == x._node;
		}
		//2.运算符重载++ --
		self& operator++()
		{
			this->_node = this->_node->next;
			return *this;
		}
		self operator++(int)
		{
			self ret = *this;
			this->_node = this->_node->next;
			return ret;
		}
		self& operator--()
		{
			this->_node = this->_node->prev;
			return *this;
		}
		self operator--(int)
		{
			self ret = *this;
			this->_node = this->_node->prev;
			return ret;
		}

		const T& operator*()
		{
			return this->_node->date;
		}

		/*self& operator=(self x)
		{
			this = x;
		}*/
	};

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

方法二:使用模板参数表示不同类型(泛型编程)

1.我们通过上面多去实现一个iterator_const_ListNode去实现const迭代器。
2.问题:代码冗余。
3.实现const迭代器就是实现一个const T& operator*返回类型的不同!

//2.节点封装-支持正向迭代器
	template<class T , class Ref>
	struct iterator_ListNode {
		//2.提供迭代器的方法:
		typedef iterator_ListNode<T,Ref> self;
		typedef ListNode<T> Node;
		Node* _node;

		iterator_ListNode(Node* x)
			:_node(x)
		{}

		//1.运算符重载:
		bool operator!=(self x)
		{
			return this->_node != x._node;
		}
		bool operator==(self x)
		{
			return this->_node == x._node;
		}
		//2.运算符重载++ --
		self& operator++()
		{
			this->_node = this->_node->next;
			return *this;
		}
		self operator++(int)
		{
			self ret = *this;
			this->_node = this->_node->next;
			return ret;
		}
		self& operator--()
		{
			this->_node = this->_node->prev;
			return *this;
		}
		self operator--(int)
		{
			self ret = *this;
			this->_node = this->_node->prev;
			return ret;
		}

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

		/*self& operator=(self x)
		{
			this = x;
		}*/
	};
//2.提供迭代器的方法:

		typedef iterator_ListNode<T,T&> iterator;
		typedef iterator_ListNode<T,const T&> const_iterator;
		typedef ListNode<T> Node;

		//2-1:迭代器应该满足左闭右开

		//List_Node<T>* 类型到iterator类型是通过单参数的构造函数支持的!
		iterator begin() { return head->next; }
		iterator end() {return head;}

		const_iterator cbegin() { return head->next; }
		const_iterator cend() { return head; }

3.拷贝构造+赋值

//1.拷贝构造:
		list(list& copy)
			:head(new ListNode<T>(T()))
		{
			head->prev = head;
			head->next = head;

			//循环copy调用push_back
			for (auto num : copy)
			{
				push_back(num);
			}
		}
//赋值相关+交换函数
		void swap(list& tmp)
		{
			Node* head = this->head;
			this->head = tmp.head;
			tmp.head = head;
		}

		list operator=(list tmp)
		{
			swap(tmp);
			return *this;
		}

2.增

1.insert

在这里插入图片描述

1.模拟实现第一个insert函数提供迭代器和插入的节点数据:

//为什么不可以iterator&类型返回
		//Node* 类型到iterator类型通过单参数的构造函数支持的:发生隐式类型转换!
		//Node* 类型到iterator&类型没有被支持的!
		iterator insert(iterator pos , T x = T())
		{
			//1.产生一个节点:
			Node* newnode = new ListNode<T>(x);
			//2.连接!
			Node* next = pos._node->next;
			pos._node->next = newnode;
			newnode->prev = pos._node;
			newnode->next = next;
			next->prev = newnode;

			return newnode;
		}

2.push_front && push_back

	void push_back(T x = T())
		{
			insert(head->prev, x);
		}
	void push_front(T x = T())
		{
			insert(head, x);
		}

3.删

1.erase

在这里插入图片描述

//2.删除考虑返回一下下一个位置的迭代器:
		iterator erase(iterator pos)
		{
			Node* prev = pos._node->prev;
			Node* next = pos._node->next;

			prev->next = next;
			next->prev = prev;

			//1.使用默认生成的析构函数
			delete pos._node;
			return next;
		}

2.pop_front && pop_back

void pop_back()
		{
			erase(head->prev);
		}

		void pop_front()
		{
			erase(head->next);
		}

4.查

1.find

iterator find(T x)
		{
			iterator cur = begin();
			while (cur != end())
			{
				if (cur._node->date == x)
					return cur;
				cur = cur._node->next;
				//单参数构造函数支持的隐式类型转换!
			}
			return nullptr;
		}

5.改:

2.amend

//修改数据:
		void amend(iterator pos,T x)
		{
			pos._node->date = x;
		}

6.析构函数:

在这里插入图片描述

1.clear 和 ~list

1.清除所有的节点数据会保留头节点。
2.使用clear后的状态应该满足只有一个哨兵位的头节点并且前驱指向自己后继指向自己。

//4.遍历链表清理节点:
		void clear()
		{
			Node* cur = head->next;
			while (cur != head)
			{
				Node* next = cur->next;
				delete cur;
				cur = next;
			}
			head->next = head;
			head->prev = head;
		}

		//析构:
		~list()
		{
			clear();
			delete head;
		}

7.容量相关的函数:

1.size()

//容量相关:
		size_t size()
		{
			assert(head != nullptr);
			int count = 0;
			if (empty())
				return 0;
			else
			{
				iterator cur = begin();
				while (cur != end())
				{
					count++;
					cur = cur._node->next;
					//单参数构造函数支持的隐式类型转换!
				}
				return count;
			}
		}

2.empty()

bool empty()
		{
			assert(head != nullptr);
			if (head->next == head)
				return true;
			return false;
		}

8.实现operator->的意义:

在这里插入图片描述

主要思路:
1.实现了一个自定义类型AB。
2.push_back多个匿名AB类型的对象到链表l1中。
3.通过迭代器遍历链表数据。
4.因为我们没有去重载AB类型的operator流插入,所以不可以正常的打印数据。

1.简单解决问题的方法:

在这里插入图片描述

2.实现operator->

1.我们知道->结构体指针类型的对象去访问成员变量的一个方法。
2.实现operator->要比对一个类型去实现operator<< operator>>方便。
3.通过->访问AA数据考虑const还是非const的?
4.给模板再加上一个参数去确定operator->返回值类型。

template<class T , class Ref , class arrows>
	struct iterator_ListNode {
		//2.提供迭代器的方法:
		typedef iterator_ListNode<T,Ref,arrows> self;
		typedef ListNode<T> Node;
		Node* _node;

		iterator_ListNode(Node* x)
			:_node(x)
		{}

		//1.运算符重载:
		bool operator!=(self x)
		{
			return this->_node != x._node;
		}
		bool operator==(self x)
		{
			return this->_node == x._node;
		}
		//2.运算符重载++ --
		self& operator++()
		{
			this->_node = this->_node->next;
			return *this;
		}
		self operator++(int)
		{
			self ret = *this;
			this->_node = this->_node->next;
			return ret;
		}
		self& operator--()
		{
			this->_node = this->_node->prev;
			return *this;
		}
		self operator--(int)
		{
			self ret = *this;
			this->_node = this->_node->prev;
			return ret;
		}

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

		arrows operator->()
		{
			return &(this->_node->date);
		}

		/*self& operator=(self x)
		{
			this = x;
		}*/
	};
//2.提供迭代器的方法:
		typedef iterator_ListNode<T,T&, T* > iterator;
		typedef iterator_ListNode<T,const T&,const T*> const_iterator;
		typedef ListNode<T> Node;

		//2-1:迭代器应该满足左闭右开

		//List_Node<T>* 类型到iterator类型是通过单参数的构造函数支持的!
		iterator begin() { return head->next; }
		iterator end() {return head;}

		const_iterator cbegin() { return head->next; }
		const_iterator cend() { return head; }

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

3.一个问题:

在这里插入图片描述

1.在这个地方去访问数据的时候应该是两个箭头。
2.第一个箭头是重载的operator->的箭头。
3.第二个箭头是返回T或者constT 去访问数据的箭头。
4.为什么只通过一个箭头就访问到数据了呢?

在这里插入图片描述

1.按照我们之前的理解方法二是没有任何问题我们想要去掉operator按照之前的理解应该转化成方法三。
2.方法三为什么是错误的呢?
3.因为我们需要提供可读性所以我们让编译器去做了操作优化成了方法一的这样的语法。
4.总结:理论上应该优化为方法三但是为了可读性所以优化为了方法一:编译器承担了一切。

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

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

相关文章

十二款·富文本编辑器:数字创作的瑞士军刀

在数字化时代&#xff0c;内容创作已经成为我们日常生活中不可或缺的一部分。无论是撰写一封电子邮件、准备一份报告、还是在社交媒体上分享心情&#xff0c;文字都是我们表达和沟通的基石。而在这个过程中&#xff0c;富文本编辑器就如同一把瑞士军刀&#xff0c;为我们提供了…

商品详情APP端原数据淘宝数据采集API接口代码接入示例

商品详情APP端原数据API接口&#xff08;接口接入入口&#xff09;的作用是提供APP端商品的详细信息&#xff0c;包括价格、描述、图片、折后价、优惠券信息等。通过调用这个API接口&#xff0c;开发者可以获取到APP端商品详情相关的数据&#xff0c;从而进行数据分析&#xff…

怎么录屏?这里有一份超详细教程!

随着科技的发展&#xff0c;录屏已经成为人们日常生活中经常需要使用的功能&#xff0c;无论是录制游戏、教学演示、在线会议等都需要用到录屏。可是您知道怎么录屏才是最好的吗&#xff1f;接下来&#xff0c;本文将为您介绍三种流行的录屏方法&#xff0c;并提供详细的分步骤…

【遥感数字图像处理(朱文泉)】各章博文链接汇总及思维导图

遥感数字图像处理课程汇总 第0章 绪论第一章 数字图像基础第二章 数字图像存储与处理第三章 空间域处理方法第四章 变换域处理方法第五章 辐射校正第六章 几何校正第七章 图像去噪声第八章 图像增强第九章 感兴趣目标及对象提取第十章 特征提取与选择第十一章 遥感数字图像分类…

Unity 编辑器篇|(九)编辑器美化类( GUIStyle、GUISkin、EditorStyles) (全面总结 | 建议收藏)

目录 1. GUIStyle1.1 参数总览1.2 样式代码 2. GUISkin2.1 参数总览2.2 创建自定义Skin 3. EditorStyles2.1 参数总览1.2 反射获取所有EditorStyles 1. GUIStyle GUIStyle是一个用于定制GUI控件样式的类&#xff0c;它包含了控件的外观属性&#xff0c;如字体、颜色、背景等。…

springboot 3 + mysql8 + flyway 数据库版本管理

1、flyway flyway官方文档地址&#xff1a;https://documentation.red-gate.com/fd 对于不怎么看文档的我来说&#xff1a; 1&#xff09;flyway是个管理数据库版本的工具&#xff0c;可以对不同环境的sql进行迁移操作。 2&#xff09;优点&#xff1a;初始化、后期数据的管理…

dpdk网络转发环境的搭建

文章目录 前言ip命令的使用配置dpdk-basicfwd需要的网络结构测试dpdk-basicfwddpdk-basicfwd代码分析附录basicfwd在tcp转发时的失败抓包信息DPDK的相关设置 前言 上手dpdk有两难。其一为环境搭建。被绑定之后的网卡没有IP&#xff0c;我如何给它发送数据呢&#xff1f;当然&a…

动物免疫(羊驼免疫)-泰克生物

在过去几十年里&#xff0c;抗体研究和应用的领域已经经历了革命性的变化。在这个进程中&#xff0c;一种特殊来源的抗体 —— 来自骆驼科动物&#xff08;包括羊驼&#xff09;的单链抗体&#xff08;也称纳米抗体&#xff09;引起了全球科学家的广泛关注。 羊驼等骆驼科动物…

vectorCast添加边界值分析测试用例

1.1创建项目成功后会自动生成封装好的函数,在这些封装好的函数上点击右键,添加边界值分析测试用例,如下图所示。 1.2生成的用例模版是不可以直接运行的,需要我们分别点击它们,让它们自动生成相应测试用例。如下图所示,分别为变化前和变化后。 1.3点击选中生成的测试用例,…

家庭教育小知识青春期孩子如何教育?

孩子进入青春期后&#xff0c;很多家长都纷纷感叹&#xff0c;与孩子相处太难了。那个曾经乖巧、黏人的孩子&#xff0c;好像一夜之间浑身长满刺&#xff0c;变得叛逆、敏感、暴躁、将父母拒之门外、甚至是辱骂、羞辱父母。 让父母更挫败的是&#xff0c;曾经通过说教、讲道理…

sqlmap使用教程(1)

一、sqlmap简介 sqlmap是一个自动化SQL注入测试工具&#xff0c;它支持的数据库有MySQL、MSSQL、Oracle、PostgreSQL、Access、IBM DB2、SQLite、Firebird、Sybase和SAP MaxDB。sqlmap默认使用以下5种SQL注入技术&#xff1a; 基于布尔的盲注&#xff1a;根据返回页面判断条件…

flink基本概念

1. Flink关键组件: 这里首先要说明一下“客户端”。其实客户端并不是处理系统的一部分&#xff0c;它只负责作业的提交。具体来说&#xff0c;就是调用程序的 main 方法&#xff0c;将代码转换成“数据流图”&#xff08;Dataflow Graph&#xff09;&#xff0c;并最终生成作业…

Kotlin 尾递归函数

函数式编程中&#xff0c;重要的概念 尾递归&#xff1a; 当一个函数 在最后调用 自身&#xff0c;称为 尾递归&#xff0c;是一种特殊的递归函数。 Kotlin 使用 tailrec 声明尾递归函数&#xff0c;可以避免 StackOverflowError 的风险。 原理是&#xff1a;通过编译器优化 …

BACnet网关BL121BN 实现稳定可靠、低成本、简单的楼宇自控协议BACnet转OPC UA解决方案

随着楼宇自控系统的迅猛发展&#xff0c;人们深刻认识到在楼宇暖通行业中&#xff0c;实时、可靠、安全的数据传输至关重要。在此背景下&#xff0c;高性能的楼宇暖通数据传输解决方案——协议转换网关应运而生&#xff0c;广泛应用于楼宇自控和暖通空调系统应用中。 钡铼技术…

Linux:动静态库的概念制作和底层工作原理

文章目录 动静态库基础认知动静态库基本概念静态库的制作库的概念包的概念 静态库的使用第三方库小结 动态库的制作动态库的使用动态库如何找到内容&#xff1f;小结 动态库加载库和程序都要加载可执行程序的地址问题地址问题逻辑地址和平坦模式绝对编址和相对编址与位置无关码…

Lombok工具包

Lombok已经集成springboot项目中因此在依赖中引入Lombok不需要指定版本号。 <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId> </dependency> Lombok中各种注释的含义

51单片机电子密码锁Proteus仿真+程序+视频+报告

目录 视频 设计分析 系统结构 仿真图 资料内容 资料下载地址&#xff1a;51单片机电子密码锁Proteus仿真程序视频报告 视频 单片机电子密码锁Proteus仿真程序视频 设计分析 (1)能够从键盘中输入密码&#xff0c;并相应地在显示器上显示‘*’&#xff1b; (2)能够判断密码…

什么是碳结算电能表?

引言 近年来&#xff0c;我国加速推进碳达峰碳中和标准计量体系建设&#xff0c;但随着各地区、各领域、各行业对碳排放核算数据的需求显著提升&#xff0c;当前碳排放核算体系数据更新偏慢、核算口径不一、基础排放因子滞后等一系列问题也开始凸显。新形势下对碳排放统计核算…

Android OpenGL EGL使用——自定义相机

如果要使用OpenGl来自定义相机&#xff0c;EGL还是需要了解下的。 可能大多数开发者使用过OpengGL但是不知道EGL是什么&#xff1f;EGL的作用是什么&#xff1f;这其实一点都不奇怪&#xff0c;因为Android中的GlSurfaceView已经将EGL环境都给配置好了&#xff0c;你一直在使用…

【机器学习】调配师:咖啡的完美预测

有一天&#xff0c;小明带着一脸期待找到了你这位数据分析大师。他掏出手机&#xff0c;屏幕上展示着一份详尽的Excel表格。“看&#xff0c;这是我咖啡店过去一年的数据。”他滑动着屏幕&#xff0c;“每个月的销售量、广告投入&#xff0c;还有当月的气温&#xff0c;我都记录…