list模拟实现(15)

news2024/11/25 7:12:17

目录

1、简单框架

1、list.h

2、test.cpp

2、list迭代器实现

1、list.h

2、test.cpp

3、思考

1、迭代器中的拷贝构造和赋值重载是否需要自己实现?析构呢?

2、体会类型的力量

3、const迭代器实现

1、list.h

2、test.cpp

4、重载迭代器的operator->

5、insert和erase接口实现

1、insert模拟实现

2、erase模拟实现

6、vector与list对比

1、vector

2、list

7、list拷贝构造、赋值重载问题

1、clear模拟实现

2、析构函数

3、深拷贝

1、传统写法

2、现代写法

8、迭代器区间和n个val初始化的构造函数冲突

1、问题

2、解决方法

9、反向迭代器模拟实现


1、简单框架

list的底层是一个双向带头循环链表

1、list.h

#pragma once
#include<iostream>
using namespace std;

namespace tutu
{
	template<class T>
	struct listNode
	{
		listNode<T>* _next;
		listNode<T>* _prev;
		T _data;

		listNode(const T& data=T())
			:_next(nullptr)
			,_prev(nullptr)
			,_data(data)
		{}
	};

	template<class T>
	class list
	{
		typedef listNode<T> Node;
	public:
		list()
			:_head(new Node)
		{
			_head->_next = _head;
			_head->_prev = _head;
		}

		void push_back(const T& x)
		{
			Node* newnode = new Node(x);
			Node* tail = _head->_prev;
			tail->_next = newnode;
			newnode->_prev = tail;
			newnode->_next = _head;
			_head->_prev = newnode;
		}
	private:
		Node* _head;
	};

	void test1()
	{
		list<int> l1;
		l1.push_back(1);
		l1.push_back(2);
		l1.push_back(3);
		l1.push_back(4);

	}
}

2、test.cpp

#include"list.h"

int main()
{
	tutu::test1();
	return 0;
}

2、list迭代器实现

迭代器是像指针一样的类型,模仿指针的行为,解引用能取数据,++能到下一个位置。

在string和vector中,迭代器就是原生指针,因为他们底层是连续的物理空间;但是list底层物理空间不是连续的,如果还用原生指针,解引用是一个节点,++到不了下一个位置,但是C++提供了类,自定义类型可以对运算符进行重载,所以list底层迭代器的实现就是对节点指针进行封装,重载operator++、operator!=和operator*。

1、list.h

#pragma once
#include<iostream>
using namespace std;

namespace tutu
{
	template<class T>
	struct listNode
	{
		listNode<T>* _next;
		listNode<T>* _prev;
		T _data;

		listNode(const T& data=T())
			:_next(nullptr)
			,_prev(nullptr)
			,_data(data)
		{}
	};

	template<class T>
	struct __list_iterator
	{
		typedef listNode<T> Node;
		Node* _node;

		__list_iterator(Node* node)
			:_node(node)
		{}

		//++it
		__list_iterator<T>& operator++()
		{
			_node = _node->_next;
			return *this;
		}

		//it++
		__list_iterator<T> operator++(int)
		{
			__list_iterator<T> tmp(*this);
			_node = _node->_next;
			return tmp;
		}

		//--it
		__list_iterator<T>& operator--()
		{
			_node = _node->_prev;
			return *this;
		}

		//it--
		__list_iterator<T> operator--(int)
		{
			__list_iterator<T> tmp(*this);
			_node = _node->_prev;
			return tmp;
		}

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

		bool operator!=(const __list_iterator<T>& it) const
		{
			return _node != it._node;
		}

		bool operator==(const __list_iterator<T>& it) const
		{
			return _node == it._node;
		}

	};

	template<class T>
	class list
	{
		typedef listNode<T> Node;
	public:
		typedef __list_iterator<T> iterator;

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

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

		list()
			:_head(new Node)
		{
			_head->_next = _head;
			_head->_prev = _head;
		}

		void push_back(const T& x)
		{
			Node* newnode = new Node(x);
			Node* tail = _head->_prev;
			tail->_next = newnode;
			newnode->_prev = tail;
			newnode->_next = _head;
			_head->_prev = newnode;
		}
	private:
		Node* _head;
	};

	void test1()
	{
		list<int> l1;
		l1.push_back(1);
		l1.push_back(2);
		l1.push_back(3);
		l1.push_back(4);
		l1.push_back(5);

		list<int>::iterator it = l1.begin();
		while (it != l1.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}
}

2、test.cpp

#include"list.h"

int main()
{
	tutu::test1();
	return 0;
}

3、思考

1、迭代器中的拷贝构造和赋值重载是否需要自己实现?析构呢?

答:都不需要。这里就是需要浅拷贝,默认生成的即可。迭代器中虽然有一个节点指针的成员,但是这个成员不归迭代器管,迭代器的意义就是访问和修改容器的。迭代器是借助节点的指针访问修改链表,节点是属于链表的,不属于迭代器,所以不归它管释放。

2、体会类型的力量

Node*原生指针和一个迭代器对象,它们占用的空间是一样大的,在32位机器上都是4个byte,并且值存的也一样,但是对他们使用运算符的意义和结果是不一样的。

3、const迭代器实现

普通对象调普通迭代器,const对象调const迭代器,目前我们只实现了普通迭代器,const迭代器还需要再写一份,传统的写法是再写一份const版本的迭代器,但是这样会造成代码冗余,这里推荐高手写法,即stl底层实现写法。

比较一下普通迭代器和const迭代器的区别,普通迭代器可读可写,const迭代器只能读不能写,体现到代码层面上就是operator*,一个是返回引用,一个是返回const引用,所以这里用了一个模板参数解决这个问题。

1、list.h

#pragma once
#include<iostream>
using namespace std;

namespace tutu
{
	template<class T>
	struct listNode
	{
		listNode<T>* _next;
		listNode<T>* _prev;
		T _data;

		listNode(const T& data=T())
			:_next(nullptr)
			,_prev(nullptr)
			,_data(data)
		{}
	};

	template<class T,class Ref>
	struct __list_iterator
	{
		//因为增加了一个模板参数,所以下面的类型都要改,所以增加一个typedef
		typedef __list_iterator<T, Ref> self;
		typedef listNode<T> Node;
		Node* _node;

		__list_iterator(Node* node)
			:_node(node)
		{}

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

		//it++
		self operator++(int)
		{
			__list_iterator<T> tmp(*this);
			_node = _node->_next;
			return tmp;
		}

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

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

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

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

		bool operator==(const self& it) const
		{
			return _node == it._node;
		}

	};

	template<class T>
	class list
	{
		typedef listNode<T> Node;
	public:
		typedef __list_iterator<T,T&> iterator;
		typedef __list_iterator<T,const T&> const_iterator;

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

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

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

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

		list()
			:_head(new Node)
		{
			_head->_next = _head;
			_head->_prev = _head;
		}

		void push_back(const T& x)
		{
			Node* newnode = new Node(x);
			Node* tail = _head->_prev;
			tail->_next = newnode;
			newnode->_prev = tail;
			newnode->_next = _head;
			_head->_prev = newnode;
		}
	private:
		Node* _head;
	};


	void Print_list(const list<int>& lt)
	{
		list<int>::const_iterator it = lt.begin();
		while (it != lt.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}

	void test1()
	{
		list<int> l1;
		l1.push_back(1);
		l1.push_back(2);
		l1.push_back(3);
		l1.push_back(4);
		l1.push_back(5);

		Print_list(l1);

	}
}

2、test.cpp

#include"list.h"

int main()
{
	tutu::test1();
	return 0;
}

4、重载迭代器的operator->

前面都是对list<int>举例,链表中放整形,如果是放一个自定义类型,那么cout<<*it 时就会出问题,例如:list<Date>,解引用是一个Date类,需要对流提取进行重载,如果不想重载可以如图:

迭代器是像指针一样的类型,所以可以对operator->进行重载,可以得到如下代码:

在__list_iterator中,对operator->进行重载,如:

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

注意:

然后对于operator->的返回值和operator*有着相同的问题,普通迭代器返回Date*,const迭代器返回const Date*,所以上述不能给T*,这里建议新增模板参数Ptr,传参时,普通迭代器传T*,const迭代器传const T*,如:

#pragma once
#include<iostream>
using namespace std;

namespace tutu
{
	template<class T>
	struct listNode
	{
		listNode<T>* _next;
		listNode<T>* _prev;
		T _data;

		listNode(const T& data=T())
			:_next(nullptr)
			,_prev(nullptr)
			,_data(data)
		{}
	};

	template<class T,class Ref,class Ptr>
	struct __list_iterator
	{
		//因为增加了一个模板参数,所以下面的类型都要改,所以增加一个typedef
		typedef __list_iterator<T, Ref,Ptr> self;
		typedef listNode<T> Node;
		Node* _node;

		__list_iterator(Node* node)
			:_node(node)
		{}

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

		//it++
		self operator++(int)
		{
			__list_iterator<T> tmp(*this);
			_node = _node->_next;
			return tmp;
		}

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

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

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

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

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

		bool operator==(const self& it) const
		{
			return _node == it._node;
		}

	};

	template<class T>
	class list
	{
		typedef listNode<T> Node;
	public:
		typedef __list_iterator<T,T&,T*> iterator;
		typedef __list_iterator<T,const T&,const T*> const_iterator;

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

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

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

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

		list()
			:_head(new Node)
		{
			_head->_next = _head;
			_head->_prev = _head;
		}

		void push_back(const T& x)
		{
			Node* newnode = new Node(x);
			Node* tail = _head->_prev;
			tail->_next = newnode;
			newnode->_prev = tail;
			newnode->_next = _head;
			_head->_prev = newnode;
		}
	private:
		Node* _head;
	};

	struct Date
	{
		int _year;
		int _month;
		int _day;

		Date(int year = 1,int month = 1,int day=1)
			:_year(year)
			,_month(month)
			,_day(day)
		{}
	};

	void Print_list2(const list<Date>& lt)
	{
		list<Date>::const_iterator it = lt.begin();
		while (it != lt.end())
		{
			cout << it->_year << "/" << it->_month << "/" << it->_day << endl;
			++it;
		}
		cout << endl;
	}

	void test2()
	{
		list<Date> l1;
		l1.push_back(Date(2022, 11, 23));
		l1.push_back(Date(2022, 11, 24));
		l1.push_back(Date(2022, 11, 25));
		l1.push_back(Date(2022, 11, 26));

		Print_list2(l1);
	}
}

5、insert和erase接口实现

1、insert模拟实现

有了insert,push_back和push_front可以复用:

2、erase模拟实现

有了erase,pop_back和pop_front就可以复用:

6、vector与list对比

1、vector

vector是连续的物理空间,是优势,也是劣势。

优势:支持高效的随机访问。

劣势:1、空间不够要增容,增容代价比较大  2、可能存在一定的空间浪费,按需申请,会导致频繁增容,所以都会扩2倍左右  3、头部或中部插入删除需要挪动数据,效率低下

2、list

list很好解决了vector以上的问题

1、按需申请释放空间  2、list任意位置支持O(1)插入删除

小结:vector和list是互补的两个数据结构。

7、list拷贝构造、赋值重载问题

1、clear模拟实现

2、析构函数

3、深拷贝

1、传统写法

2、现代写法

1、支持迭代器区间初始化的构造函数

2、拷贝构造

3、赋值重载

8、迭代器区间和n个val初始化的构造函数冲突

1、问题

n个val初始化的构造函数:

目前我们的代码中,已经写了支持一段迭代器区间初始化和n个val初始化的构造函数,但是当写以下代码时,编译器会报错:

这就是经典的,迭代器区间初始化和n个val初始化的构造函数的冲突问题:

小结:1、整形的字面常量默认为int类型的,浮点数的字面常量默认为double类型。  2、编译器匹配的原则:只会找更匹配的。

2、解决方法

法一:将size_t  n改为int  n

法二:提供一个int  n的重载版本

9、反向迭代器模拟实现

反向迭代器跟正向迭代器的区别就是++、--的方向是反的,所以反向迭代器封装正向迭代器即可,控制重载++、--的方向。

list库里的底层rbegin、rend跟begin、end是对称的。所以operator*取前一个位置,主要就是为了让反向迭代器开始和结束跟正向迭代器对称。

因为反向迭代器是对正向迭代器的一个封装,所以可以做成模板,iterator是哪个容器的迭代器,reverse_iterator<iterator>就可以适配出那个容器的反向迭代器,这是复用的体现。


reverse_iterator.h

#pragma once
namespace tutu
{
	template<class Iterator,class Ref,class Ptr>
	class reverse_iterator
	{
		typedef reverse_iterator<Iterator,Ref,Ptr> self;
	public:
		reverse_iterator(Iterator it)
			:_it(it)
		{}

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

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

		bool operator!=(const self& rit) const
		{
			return _it != rit._it;
		}

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

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

	private:
		Iterator _it;
	};
}

在list.h中包一下上述头文件,再增加两行typedef,就可以实现反向迭代器

我们这里有三个模板参数,而库里只有一个,这里是做了简化,因为库里的比较麻烦。

注意:

1、一个类里面要取它里面的东西,只能取它的成员变量和内嵌类型,模板参数可通过typedef取到。 

2、要取模板参数中的内嵌类型,编译器这里肯定是通不过的,因为编译器编译到这里还没有实例化,凡是要取一个模板参数里的内嵌类型(内部类或typedef的),加个typename是告诉编译器,它是一个类型,先让它通过,等实例化时,再去找它里面的类型。

3、如果上述按照库中实现,定义一个模板参数,在__list_iterator定义了内嵌类型,但是vector、string就不能直接套,它们底层是原生指针,不是自定义类型,所以还是三个模板参数比较好。vector的迭代器是原生指针,无法取内部类型,那么上面的实现就完了,stl源代码中是使用了一个叫迭代器萃取的技术解决的。

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

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

相关文章

【附源码】计算机毕业设计JAVA知识库系统

【附源码】计算机毕业设计JAVA知识库系统 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JAVA mybatis …

python 使用enumerate()函数详解

一、enumerate() 函数简介 enumerate()是python的内置函数&#xff0c;将一个可遍历iterable数据对象(如list列表、tuple元组或str字符串)组合为一个索引序列&#xff0c;同时列出数据和数据下标&#xff0c;一般用在for循环当中。 函数返回一个enumerate对象&#xff0c;是一…

Metabase学习教程:仪表盘-4

交叉筛选&#xff1a;图表联动更新仪表盘筛选器 只需单击几下&#xff0c;就可以配置任何图表或表以更新仪表盘筛选器。 Metabase允许您自定义当您单击仪表盘部件。本文将详细介绍如何设置部件更新仪表盘上筛选器在我们称之为交叉筛选. 下面是我们要连接的仪表盘&#xff1a…

如何使用轻量应用服务器搭建Veno File Manager个人私有云网盘?

之前有介绍过使用可道云搭建个人云网盘系统&#xff1a;如何使用闲置的云服务器搭建一个属于自己的可道云私人云网盘&#xff0c;这个教程我将介绍使用Veno File Manager搭建个人云网盘&#xff0c;搭建的过程很相似&#xff0c;都采用的是宝塔面板作为辅助&#xff0c;可道云搭…

40 行 Python 代码,写一个 CPU

一、引言 CPU 如何工作&#xff1f;是困扰初级用户一个迷雾般的难题。我们可能知道诸如程序计数器、RAM、寄存器的只言片语&#xff0c;但尚未对这些部件的工作原理及整个系统的协同有清晰和总体的认识。 本文使用四十行 Python 代码来实现一个最简单的 CPU。使它可编程&…

红眼睛微型成像仪拍照、存储与参数复位

拍照在 IFD-x 设备中有两种含义&#xff0c;一是将照片保存于设备内部的存储器&#xff08;硬拍照&#xff09;&#xff0c;二是将照片 保存于计算机或者手机&#xff08;软拍照&#xff09;。 &#xff08;1&#xff09;硬拍照 有两种方法来触发一次硬拍照&#xff0c;一…

【树莓派不吃灰】Linux篇⑩ 学习例行性工作排程(核心概念)

目录1. 什么是例行性工作排程2. 仅运行一次的工作排程3. 循环运行的例行性工作排程4. 可唤醒停机期间的工作任务5. 重点回顾❤️ 博客主页 单片机菜鸟哥&#xff0c;一个野生非专业硬件IOT爱好者 ❤️❤️ 本篇创建记录 2022-11-28 ❤️❤️ 本篇更新记录 2022-11-28 ❤️&…

5、python中的文件操作

文章目录文件操作介绍文件的打开操作open()文件的关闭操作os模块关于操作系统关于路径json模块将python对象编码成json字符串序列化和反序列化常用参数将json字符串解码为python对象存储为excel文件文件操作介绍 文件的作用&#xff1a;数据的持久化存储 一个程序在运行过程中…

小满Vue3第四十六章(Proxy跨域)

1.首先我们先了解一下什么是跨域 主要是出于浏览器的同源策略限制&#xff0c;它是浏览器最核心也最基本的安全功能。 当一个请求url的 协议、域名、端口 三者之间任意一个与当前页面url不同即为跨域。 例如 xxxx.com -> xxxx.com 存在跨域 协议不同 例如 127.x.x.x:800…

Dubbo3.0新特性

服务注册模型 注册模型从接口级别服务注册改为 应用级别服务之策 应用级服务发现简介 概括来说&#xff0c;Dubbo3 引入的应用级服务发现主要有以下优势 适配云原生微服务变革。云原生时代的基础设施能力不断向上释放&#xff0c;像 Kubernetes 等平台都集成了微服务概念抽…

关于各种PLMN的选择

RAT&#xff1a;Radio Access Technology RPLMN&#xff1a;Registered PLMN 终端在上次关机或脱网前登记上的PLMN,会临时保存在USIM卡上 HPLMN: Home PLMN 用户USIM对应IMSI的PLMN EHPLMN:EquivalentHome PLMN,HPLMN对应的运营商可能会有不同的号段&#xff0c;例如中国移动有…

【软考】-- 操作系统(中)

操作系统&#xff08;中&#xff09;:第三节 存储管理&#x1f380;一、存储管理的基本概念1️⃣存储管理2️⃣存储方式分类&#xff1a;3️⃣相对地址4️⃣相对地址空间通过地址再定位机构转换到绝对地址空间&#xff08;物理地址空间&#xff09;&#x1f381;二、存储方式&a…

WFST--学习笔记

(Weighted Finite-State Transducer)&#xff1a;加权有限状态转换机&#xff0c;由有限状态接收机(FSA)拓展而来&#xff0c;在ASR领域常被称为“解码器”。是一个包含了声学模型&#xff08;H&#xff09;、上下文相关处理的FST&#xff08;context-dependency transducer, C…

手画图解 | 关于死锁,面试的一切都在这里了

什么是死锁&#xff08;Deadlock&#xff09; 死锁是指两个或两个以上的线程在执行过程中&#xff0c;因争夺资源而造成的一种互相等待的现象。若无外力作用&#xff0c;它们都将无法推进下去。 产生死锁的四个必要条件得烂熟于心&#xff1a; 互斥条件&#xff1a;进程要求对…

艾美捷QuickTiter 逆转录病毒定量试剂盒测定原理

逆转录病毒基因转移是一种有效地将稳定的、可遗传的遗传物质导入大肠杆菌的技术任何分裂细胞类型的基因组。不能复制的逆转录病毒通常通过将逆转录病毒载体转染到包装细胞系中。逆转录病毒根据用于进入宿主细胞的受体。亲嗜性病毒可以识别仅在小鼠身上发现的受体和大鼠细胞。兼…

【信息融合】基于matlab BP神经网络和DS证据理论不确定性信息融合问题【含Matlab源码 2204期】

⛄一、 D-S证据理论及解释 证据理论由Dempster在1967年最初提出,并由他的学生Shafer改进推广使之成为符合有限离散领域中推理的形式,因此称为D-S理论。证据理论讨论一个“辨识框架”(Frame of Discernment)Θ,它是关于命题的相互独立的可能答案或假设的一个有限集合。按传统方…

Qml中的那些坑(三)---MouseArea上的ListView滚轮事件穿透

【写在前面】 最近在 Qml 中使用 MouseArea 时发现了一个奇怪的现象&#xff1a; 位于 MouseArea 上的 ListView 在处理了滚轮事件的情况下进行滚轮&#xff0c;下面的 MouseArea 却在某些情况下接收到了这个事件。 按照直觉&#xff0c;ListView 明明有内部的滚轮事件处理&…

Cesium中的DataSource和Entity关系

本章主要探讨一下Cesium中的DataSource和Entity。 介绍 首先简单说一下Entity与Primitive。 Cesium为开发者提供了丰富的图形绘制和空间数据管理的API&#xff0c;可以分为两类&#xff0c;一类是面向图形开发人员的低层次API&#xff0c;通常被称为Primitive API&#xff0…

Java基础知识点整理

一、Java概述 1、何为编程 编程就是让计算机为解决某个问题而使用某种程序设计语言编写程序代码&#xff0c;并最终得到结果的过程。 为了使计算机能够理解人的意图&#xff0c;人类就必须要将需解决的问题的思路、方法、和手段通过计算机能够理解的形式告诉计算机&#xff…

助力工业物联网,工业大数据项目介绍及环境构建【一】

文章目录工业大数据项目介绍及环境构建01&#xff1a;专栏目标02&#xff1a;项目背景03&#xff1a;项目需求04&#xff1a;业务流程05&#xff1a;技术选型06&#xff1a;Docker的介绍07&#xff1a;Docker的网络08&#xff1a;Docker的使用09&#xff1a;Oracle的介绍10&…