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

[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.mfbz.cn/a/339422.html

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

相关文章

PyTorch深度学习实战(31)——生成对抗网络(Generative Adversarial Network, GAN)

PyTorch深度学习实战&#xff08;31&#xff09;——生成对抗网络 0. 前言1. GAN2. GAN 模型分析3. 利用 GAN 模型生成手写数字小结系列链接 0. 前言 生成对抗网络 (Generative Adversarial Networks, GAN) 是一种由两个相互竞争的神经网络组成的深度学习模型&#xff0c;它由…

EOCR电机保护器带煤电厂的具体应用

EOCR系列电动机智能保护器在煤电厂中有广泛的应用。这些保护器具有齐全的保护功能、直观的测量参数、快速的反应灵敏度、可靠的行动以及与上位机通讯构成远程监控的能力&#xff0c;使其成为理想的低压电动机保护及远程监控产品。 在煤电厂中&#xff0c;电动机保护器需要具备过…

SpringCloud Aliba-Sentinel【上篇】-从入门到学废【4】

&#x1f3b5;诗词分享&#x1f3b5; 大江东去&#xff0c;浪淘尽&#xff0c;千古风流人物。 ——苏轼《念奴娇赤壁怀古》 目录 &#x1f37f;1.Sentinel是什么 &#x1f9c2;2.特点 &#x1f9c8;3.下载 &#x1f32d;4.sentinel启动 &#x1f953;5.实例演示 1.Senti…

IOT pwn

已经过了填坑的黄金时期 环境搭建 交叉编译工具链 很多开源项目需要交叉编译到特定架构上&#xff0c;因此需要安装对应的交叉编译工具链。 sudo apt install gcc-arm-linux-gnueabi g-arm-linux-gnueabi -y sudo apt install gcc-aarch64-linux-gnu g-aarch64-linux-gnu -…

RK3568笔记十:Zlmediakit交叉编译

若该文为原创文章&#xff0c;转载请注明原文出处。 编译Zlmediakit的主要目的是想实现在RK3568拉取多路RTPS流&#xff0c;并通过MPP硬解码&#xff0c;DRM显示出来。为了实现拉取多路流选择了Zlmediakit,使用FFMEPG也可以&#xff0c;在RV1126上已经验证了可行性。 一、环境…

对MODNet 主干网络 MobileNetV2的剪枝探索

目录 1 引言 1.1 MODNet 原理 1.2 MODNet 模型分析 2 MobileNetV2 剪枝 2.1 剪枝过程 2.2 剪枝结果 2.2.1 网络结构 2.2.2 推理时延 2.3 实验结论 3 模型嵌入 3.1 模型保存与加载 法一&#xff1a;保存整个模型 法二&#xff1a;仅保存模型的参数 小试牛刀 小结…

服务端实现微信小游戏登录

1 微信小程序用户登录及其流程 小程序可以通过微信官方提供的登录能力,便能方便的获取微信提供的用户身份标识,达到建立用户体系的作用。 官方文档提供了登录流程时序图,如下: 从上述的登录流程时序图中我们发现,这里总共涉及到三个概念。 第一个是小程序,小程序即我们…

C#,入门教程(30)——扎好程序的笼子,错误处理 try catch

上一篇&#xff1a; C#&#xff0c;入门教程(29)——修饰词静态&#xff08;static&#xff09;的用法详解https://blog.csdn.net/beijinghorn/article/details/124683349 程序员语录&#xff1a;凡程序必有错&#xff0c;凡有错未必改&#xff01; 程序出错的原因千千万&…

Rockchip linux USB 驱动开发

Linux USB 驱动架构 Linux USB 协议栈是一个分层的架构&#xff0c;如下图 5-1 所示&#xff0c;左边是 USB Device 驱动&#xff0c;右边是 USB Host 驱动&#xff0c;最底层是 Rockchip 系列芯片不同 USB 控制器和 PHY 的驱动。 Linux USB 驱动架构 USB PHY 驱动开发 USB 2…

LeetCode 77. 组合

77. 组合 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;n 4, k 2 输出&#xff1a; [[2,4],[3,4],[2,3],[1,2],[1,3],[1,4], ] 示例 2&#xff1a; 输入&#xff1a;…

Kafka(八)使用Kafka构建数据管道

目录 1 使用场景2 构建数据管道时需要考虑的问题2.1 及时性2.2 可靠性高可用可靠性数据传递 2.3 高吞吐量2.4 数据格式2.5 转换ETLELT 2.6 安全性2.7 故障处理2.8 耦合性和灵活性临时数据管道元数据丢失末端处理 3 使用Connect API3.1 Connect的数据处理流程sourcesinkconnecto…

csrf漏洞之DedeCMS靶场漏洞(超详细)

1.Csrf漏洞&#xff1a; 第一步&#xff0c;查找插入点&#xff0c;我选择了网站栏目管理的先秦两汉作为插入点 第二步修改栏目名称 第三步用burp拦截包 第四步生成php脚本代码 第五步点击submit 第六步提交显示修改成功 第二个csrf 步骤与上述类似 红色边框里面的数字会随着…

第91讲:MySQL主从复制集群主库与从库状态信息的含义

文章目录 1.主从复制集群正常状态信息2.从库状态信息中重要参数的含义 1.主从复制集群正常状态信息 通过以下命令查看主库的状态信息。 mysql> show processlist;在主库中查询当前数据库中的进程&#xff0c;看到Master has sent all binlog to slave; waiting for more u…

Rocky Linux 8.9 安装图解

风险告知 本人及本篇博文不为任何人及任何行为的任何风险承担责任&#xff0c;图解仅供参考&#xff0c;请悉知&#xff01;本次安装图解是在一个全新的演示环境下进行的&#xff0c;演示环境中没有任何有价值的数据&#xff0c;但这并不代表摆在你面前的环境也是如此。生产环境…

2023 IoTDB Summit:北京城建智控科技股份有限公司高级研发主管刘喆《IoTDB在城市轨道交通综合监控系统中的应用》...

12 月 3 日&#xff0c;2023 IoTDB 用户大会在北京成功举行&#xff0c;收获强烈反响。本次峰会汇集了超 20 位大咖嘉宾带来工业互联网行业、技术、应用方向的精彩议题&#xff0c;多位学术泰斗、企业代表、开发者&#xff0c;深度分享了工业物联网时序数据库 IoTDB 的技术创新…

Laykefu客服系统 任意文件上传漏洞复现

0x01 产品简介 Laykefu 是一款基于workerman+gatawayworker+thinkphp5搭建的全功能webim客服系统,旨在帮助企业有效管理和提供优质的客户服务。 0x02 漏洞概述 Laykefu客服系统/admin/users/upavatar.html接口处存在文件上传漏洞,而且当请求中Cookie中的”user_name“不为…

SpringBoot+Email发送邮件

引言 邮件通知是现代应用中常见的一种通信方式&#xff0c;特别是在需要及时反馈、告警或重要事件通知的场景下。Spring Boot提供了简单而强大的邮件发送功能&#xff0c;使得实现邮件通知变得轻而易举。本文将研究如何在Spring Boot中使用JavaMailSender实现邮件发送&#xf…

geemap学习笔记052:影像多项式增强

前言 下面介绍的主要内容是应用Image.polynomial() 对图像进行多项式增强。 1 导入库并显示地图 import ee import geemap ee.Initialize() Map geemap.Map(center[40, -100], zoom4)2 多项式增强 # 使用函数 -0.2 2.4x - 1.2x^2 对 MODIS 影像进行非线性对比度增强。# L…

Oracle Linux 7.9 安装图解

风险告知 本人及本篇博文不为任何人及任何行为的任何风险承担责任&#xff0c;图解仅供参考&#xff0c;请悉知&#xff01;本次安装图解是在一个全新的演示环境下进行的&#xff0c;演示环境中没有任何有价值的数据&#xff0c;但这并不代表摆在你面前的环境也是如此。生产环境…

画眉(京东科技设计稿转代码平台)介绍

前言 随着金融App业务的不断发展&#xff0c;为了满足不同场景下的用户体验及丰富的业务诉求&#xff0c;业务产品层面最直接体现就是大量新功能的上线及老业务的升级&#xff0c;随之也给研发带来了巨大的压力&#xff0c;所以研发效率的提升就是当前亟需解决的问题&#xff…
最新文章