【STL】:list的模拟实现

news2024/11/26 22:47:03

朋友们、伙计们,我们又见面了,本期来给大家解读一下有关list的模拟实现,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成!

C 语 言 专 栏:C语言:从入门到精通

数据结构专栏:数据结构

个  人  主  页 :stackY、

C + + 专 栏   :C++

Linux 专 栏  :Linux

目录

1. 基本构造

2. 正向迭代器

2.1 非const迭代器

2.2 const迭代器

2.3 正向迭代器的改进优化

3. 修改相关接口

3.1 insert、erase

3.2 尾插、头插、尾删、头删、清理

4. 拷贝构造、赋值重载、析构

5. 完整代码


1. 基本构造

list的底层其实是一个带头双向循环的链表,所以在模拟实现之前可以看一下库里面怎么实现的:

namespace ywh
{
	//链表结构
	template<class T>
	struct list_node
	{
		T _data;                 //节点中的数据
		list_node<T>* _prev;    //指向前一个节点的指针
		list_node<T>* _next;    //指向后一个节点的指针

        //构造
		list_node(const T& x = T())
			:_data(x)
			, _prev(nullptr)
			, _next(nullptr)
		{}

	};

	//list结构
	template<class T>
	class list
	{
	public:
		typedef list_node<T> Node;
	public:
		
		//空初始化
		void empty_init()
		{
			_head = new Node;
			_head->_prev = _head;
			_head->_next = _next;
		}
		//构造
		list()
		{
			empty_init();
		}

	private:
		Node* _head;  //链表的头节点
		size_t _size; //节点个数
	};
}

2. 正向迭代器

在使用list的阶段的迭代器使用方法和之前的string、vector一样,但是了解过链表的结构都知道,链表要访问下一个节点就是使用节点中的next指针指向下一个节点,需要访问里面的数据需要找到里面的data,++和解引用并不能访问链表的节点,那么迭代器的使用在底层肯定是封装加运算符重载。

在实现迭代器的时候也需要采用封装和运算符重载,运算符重载需要用到++、--、!=、==、*、->等运算符。迭代器分为const迭代器和非const迭代器,首先先来看看非const迭代器:

2.1 非const迭代器

迭代器的实现一般使用struct来进行封装:

namespace ljm
{
	//链表结构
	template<class T>
	struct list_node
	{
		T _data;                 //节点中的数据
		list_node<T>* _prev;    //指向前一个节点的指针
		list_node<T>* _next;    //指向后一个节点的指针

		//构造
		list_node(const T& x = T())
			:_data(x)
			, _prev(nullptr)
			, _next(nullptr)
		{}

	};

	//非const正向迭代器
	template<class T>
	struct __list_iterator
	{
		typedef list_node<T> Node;
		typedef __list_iterator<T> self;
		Node* _node;
		//迭代器构造
		__list_iterator(Node* node)
			:_node(node)
		{}
		
		//前置
		//operator++
		self& operator++()
		{
			return _node->_next;
		}
		//operator--
		self& operator--()
		{
			return _node->_prev;
		}

		//后置
		self operator++(int)
		{
			self* tmp(_node);
			_node = _node->_next;
			return tmp;
		}
		//operator--
		self operator--(int)
		{
			self* tmp(_node);
			_node = _node->_prev;
			return tmp;
		}

		//operator*
		T& operator*()
		{
			return _node->_data;
		}
		//operator->
		T* operator->()
		{
			return &_node->_data;
		}
		//operator!=
		bool operator!=(const self& s)
		{
			return _node != s._node;
		}
		//operator==
		bool operator==(const self& s)
		{
			return _node == s._node;
		}
	};

	//list结构
	template<class T>
	class list
	{
	public:
		typedef list_node<T> Node;
	public:
		
		//空初始化
		void empty_init()
		{
			_head = new Node;
			_head->_prev = _head;
			_head->_next = _head;
		}
		//构造
		list()
		{
			empty_init();
		}
        ///正向迭代器
		iterator begin()
		{
			return iterator(_head->_next); //使用匿名对象进行构造
		}
		iterator end()
		{
			return iterator(_head);
		}

	private:
		Node* _head;  //链表的头节点
		size_t _size; //节点个数
	};
}

2.2 const迭代器

迭代器中涉及到修改的就是*和->,const迭代器的作用是指向的内容不能修改,本身不能修改,所以需要重新进行封装:

namespace ljm
{
	//链表结构
	template<class T>
	struct list_node
	{
		T _data;                 //节点中的数据
		list_node<T>* _prev;    //指向前一个节点的指针
		list_node<T>* _next;    //指向后一个节点的指针

		//构造
		list_node(const T& x = T())
			:_data(x)
			, _prev(nullptr)
			, _next(nullptr)
		{}

	};

	//非const正向迭代器
	//...

	//const正向迭代器
	template<class T>
	struct __list_const_iterator
	{
		typedef list_node<T> Node;
		typedef __list_const_iterator<T> self;
		Node* _node;
		//迭代器构造
		__list_const_iterator(Node* node)
			:_node(node)
		{}

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

		//后置
		self operator++(int)
		{
			self* tmp(_node);
			_node = _node->_next;
			return tmp;
		}
		//operator--
		self operator--(int)
		{
			self* tmp(_node);
			_node = _node->_prev;
			return tmp;
		}

		//operator*
		const T& operator*()
		{
			return _node->_data;
		}
		//operator->
		const T* operator->()
		{
			return &_node->_data;
		}
		//operator!=
		bool operator!=(const self& s)
		{
			return _node != s._node;
		}
		//operator==
		bool operator==(const self& s)
		{
			return _node == s._node;
		}
	};

	//list结构
	template<class T>
	class list
	{
	public:
		typedef list_node<T> Node;
		typedef __list_iterator<T> iterator;
		typedef __list_const_iterator<T> const_iterator;
	public:
		基本构造///
		//空初始化
		void empty_init()
		{
			_head = new Node;
			_head->_prev = _head;
			_head->_next = _head;
		}
		//构造
		list()
		{
			empty_init();
		}
		///正向迭代器
		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);
		}

	private:
		Node* _head;  //链表的头节点
		size_t _size; //节点个数
	};
}

2.3 正向迭代器的改进优化

上面的这两种写法不难看出有很多代码都是重复出现的,使得代码非常冗余,那么怎么将这些冗余的代码进行合并呢?

我们可以看一下库里面如何实现:

采用泛型编程,使用模板,将这一转化的工作交给编译器,而我们只需传递const参数和非const参数即可:

namespace ywh
{
	//链表结构
	template<class T>
	struct list_node
	{
		T _data;                 //节点中的数据
		list_node<T>* _prev;    //指向前一个节点的指针
		list_node<T>* _next;    //指向后一个节点的指针

		//构造
		list_node(const T& x = T())
			:_data(x)
			, _prev(nullptr)
			, _next(nullptr)
		{}

	};

	//非const正向迭代器
	//   类型模板参数   传递引用      传递指针
	template<class T, class Ref, class Ptr>
	struct __list_iterator
	{
		typedef list_node<T> Node;
		typedef __list_iterator<T, Ref, Ptr> self;
		Node* _node;
		//迭代器构造
		__list_iterator(Node* node)
			:_node(node)
		{}
		
		//前置
		//operator++
		self& operator++()
		{
			_node = _node->_next;
            return *this;
		}
		//operator--
		self& operator--()
		{
			_node = _node->_prev;
            return *this;
		}

		//后置
		self operator++(int)
		{
			self* tmp(_node);
			_node = _node->_next;
			return tmp;
		}
		//operator--
		self operator--(int)
		{
			self* tmp(_node);
			_node = _node->_prev;
			return tmp;
		}

		//operator*
		Ref operator*()
		{
			return _node->_data;
		}
		//operator->
		Ptr operator->()
		{
			return &_node->_data;
		}
		//operator!=
		bool operator!=(const self& s)
		{
			return _node != s._node;
		}
		//operator==
		bool operator==(const self& s)
		{
			return _node == s._node;
		}
	};

	//list结构
	template<class T>
	class list
	{
	public:
		typedef list_node<T> Node;
		typedef __list_iterator<T, T&, T*> iterator;   //非const迭代器
		typedef __list_iterator<T, const T&, const T*> const_iterator;  //const迭代器
	public:
		基本构造///
		//空初始化
		void empty_init()
		{
			_head = new Node;
			_head->_prev = _head;
			_head->_next = _head;
		}
		//构造
		list()
		{
			empty_init();
		}
		///正向迭代器
		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);
		}

	private:
		Node* _head;  //链表的头节点
		size_t _size; //节点个数
	};
}

3. 修改相关接口

3.1 insert、erase

在pos位置进行插入删除比较简单,需要进行链接新节点,改变节点中指针的指向即可,库里面对于插入和删除都带有返回值,那么我们在实现的时候也加上返回值:

///修改相关接口
		//在pos位置插入节点
		iterator insert(iterator pos, const T& x)
		{
			//创建新新节点
			Node* newnode = new Node(x);
			//链接节点
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
			//更新节点个数
			++_size;
			//返回新节点的位置
			return iterator(newnode);
		}
		//删掉pos位置的节点
		iterator erase(iterator pos)
		{
			//保存相对位置
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;
			//链接节点
			delete cur;
			prev->_next = next;
			next->_prev = prev;
			//更新节点个数
			--_size;
			//返回pos的下一个位置
			return iterator(next);
		}

在进行insert之后可以看到迭代器是不会失效的,但是在erase之后pos位置会被释放,因此erase之后迭代器会失效,在使用前先更新迭代器。

3.2 尾插、头插、尾删、头删、清理

当实现了insert和erase之后,实现这些接口直接复用即可:

        //尾插
		void push_back(const T& x)
		{
			insert(end(), x);
		}
		//头插
		void push_front(const T& x)
		{
			insert(begin(), x);
		}
		//尾删
		void pop_back()
		{
			erase(--end());
		}
		//头删
		void pop_front()
		{
			erase(begin());
		}
		//清理
		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}
		}

4. 拷贝构造、赋值重载、析构

在这里我们都是采用现代写法:

 拷贝构造直接使用迭代器依次遍历进行尾插。

//拷贝构造
		list(const list<T>& lt)
		{
			//先创建空节点
			empty_init();
			//依次尾插即可
			for (auto e : lt)
			{
				push_back(e);
			}
		}
		//operator=
		void swap(list<T>& lt)
		{
			std::swap(_head, lt._head);
			std::swap(_size, lt._size);
		}
		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}
		//析构
		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
			_size = 0;
		}

5. 完整代码

 头文件:List.h

#pragma once

namespace ywh
{
	//链表结构
	template<class T>
	struct list_node
	{
		T _data;                 //节点中的数据
		list_node<T>* _prev;    //指向前一个节点的指针
		list_node<T>* _next;    //指向后一个节点的指针

		//构造
		list_node(const T& x = T())
			:_data(x)
			, _prev(nullptr)
			, _next(nullptr)
		{}

	};

	//非const正向迭代器
	//   类型模板参数   传递引用      传递指针
	template<class T, class Ref, class Ptr>
	struct __list_iterator
	{
		typedef list_node<T> Node;
		typedef __list_iterator<T, Ref, Ptr> self;
		Node* _node;
		//迭代器构造
		__list_iterator(Node* node)
			:_node(node)
		{}
		
		//前置
		//operator++
		self& operator++()
		{
			_node = _node->_next;
			return *this;
		}
		//operator--
		self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}

		//后置
		self operator++(int)
		{
			self* tmp(_node);
			_node = _node->_next;
			return tmp;
		}
		//operator--
		self operator--(int)
		{
			self* tmp(_node);
			_node = _node->_prev;
			return tmp;
		}

		//operator*
		Ref operator*()
		{
			return _node->_data;
		}
		//operator->
		Ptr operator->()
		{
			return &_node->_data;
		}
		//operator!=
		bool operator!=(const self& s)
		{
			return _node != s._node;
		}
		//operator==
		bool operator==(const self& s)
		{
			return _node == s._node;
		}
	};

	//list结构
	template<class T>
	class list
	{
	public:
		typedef list_node<T> Node;
		typedef __list_iterator<T, T&, T*> iterator;   //非const迭代器
		typedef __list_iterator<T, const T&, const T*> const_iterator;  //const迭代器
	public:
		基本构造///
		//空初始化
		void empty_init()
		{
			_head = new Node;
			_head->_prev = _head;
			_head->_next = _head;
		}
		//构造
		list()
		{
			empty_init();
		}
		//拷贝构造
		list(const list<T>& lt)
		{
			//先创建空节点
			empty_init();
			//依次尾插即可
			for (auto e : lt)
			{
				push_back(e);
			}
		}
		//operator=
		void swap(list<T>& lt)
		{
			std::swap(_head, lt._head);
			std::swap(_size, lt._size);
		}
		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}
		//析构
		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
			_size = 0;
		}
		///正向迭代器
		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);
		}
		///修改相关接口
		//在pos位置插入节点
		iterator insert(iterator pos, const T& x)
		{
			//创建新新节点
			Node* newnode = new Node(x);
			//链接节点
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
			//更新节点个数
			++_size;
			//返回新节点的位置
			return iterator(newnode);
		}
		//删掉pos位置的节点
		iterator erase(iterator pos)
		{
			//保存相对位置
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;
			//链接节点
			delete cur;
			prev->_next = next;
			next->_prev = prev;
			//更新节点个数
			--_size;
			//返回pos的下一个位置
			return iterator(next);
		}
		//尾插
		void push_back(const T& x)
		{
			insert(end(), x);
		}
		//头插
		void push_front(const T& x)
		{
			insert(begin(), x);
		}
		//尾删
		void pop_back()
		{
			erase(--end());
		}
		//头删
		void pop_front()
		{
			erase(begin());
		}
		//清理
		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}
		}
		//节点个数
		size_t size()
		{
			return _size;
		}
	private:
		Node* _head;  //链表的头节点
		size_t _size; //节点个数
	};
}

朋友们、伙计们,美好的时光总是短暂的,我们本期的的分享就到此结束,欲知后事如何,请听下回分解~,最后看完别忘了留下你们弥足珍贵的三连喔,感谢大家的支持!  

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

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

相关文章

【报错-已解决】Resource tagsets not found.

最近在接触nltk&#xff0c;已经在电脑里download nltk.data了&#xff0c;但程序还是报错Resource tagsets not found. 在电脑里查找tagset&#xff0c;发现tagset文件没有解压。 对tagset压缩包进行解压&#xff0c;并确认解压后的文件夹文件路径没有重复(tagsets)&#xf…

深入了解 CPU 的型号、代际架构与微架构

大家好&#xff0c;我是飞哥&#xff01; 在 10 月 16 号的时候&#xff0c;Intel 正式发布了第 14 代的酷睿处理器。但还有很多同学看不懂这种发布会上发布的各种 CPU 参数。借着这个时机&#xff0c;我给大家深入地讲讲 CPU 的型号规则、代际架构与微架构方面的知识。 CPU 在…

redis教程 一 redis中的常用命令

文章目录 redis常见命令Redis数据结构介绍redis通用命令String类型String的常见命令Key结构 Hash类型List类型Set类型SortedSet类型 redis常见命令 Redis数据结构介绍 Redis是一个key-value的数据库&#xff0c;key一般是String类型&#xff0c;不过value的类型多种多样&…

IIS中间件漏洞----DotNetScan 工具

用DotNetScan 工具把目标网站IP&#xff0c;端口填写下。找到IIS的网站后就可进行操作上传。 也可以使用IISPutScanner工具 上传文件 利用解析漏洞 利用漏洞的前提条件&#xff1a;网站根目录可写入

教培行业的创新:微信CRM系统带来高效管理

在当今快速发展的科技环境下&#xff0c;教育培训行业面临着越来越多的挑战和机遇。如何提高管理效率、扩大市场份额、提升客户满意度&#xff0c;成为了教培行业亟待解决的问题。而微信CRM系统的出现&#xff0c;为教培行业带来了创新性的解决方案。 一些教育培训行业存在的问…

布雷斯悖论和借贷式拥塞控制

先看布雷斯悖论&#xff0c;新增一条路不但没减少交通延滞&#xff0c;反而降低了服务水准&#xff0c;下面一个简单的例子&#xff1a; 关于布雷斯悖论的讨论已经太多&#xff0c;我给出个新解释&#xff0c;这和我引出 借贷式拥塞控制 (差论证和编码)有关。 看一个不严谨但更…

在SpringBoot 中集成Swagger

前提&#xff1a;我的SpringBoot 项目的版本是 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.18-SNAPSHOT</version><relativePath /> <!-- l…

使用Bazel构建高效的Android应用程序

使用Bazel构建高效的Android应用程序 1. 引言 在现代软件开发中&#xff0c;构建工具是至关重要的一环。Bazel是一个开源的构建工具&#xff0c;由Google开发并用于其内部项目多年。它旨在提供高效、可扩展和可靠的构建解决方案。Bazel最初专注于构建大规模软件项目&#xff…

约数之和 (普通快速幂求逆元做法)

假设现在有两个自然数 A 和 B&#xff0c;S 是 AB 的所有约数之和。 请你求出 Smod9901 的值是多少。 输入格式 在一行中输入用空格隔开的两个整数 A 和 B 。 输出格式 输出一个整数&#xff0c;代表 Smod9901 的值。 数据范围 0≤A,B≤5107 输入样例&#xff1a; …

月报总结|Moonbeam 10月份大事一览

万圣节快乐&#xff01;时间一晃眼&#xff0c;10月已经迈入尾声&#xff0c;也即将迎来寒冷的冬天。但与季节相反&#xff0c;加密产业近期的发展可以说是高潮起伏&#xff0c;热度不断攀升。Moonbeam在10月中也发布了许多重大的更新&#xff0c;如Uniswap V3前段上线、众贷DO…

找人做软件,过程该怎么控制?

我们找外包公司或者程序员兼职做软件&#xff0c;我们该怎么做过程控制呢&#xff0c; 做过程控制的目的是确保最终交付的软件是我们需要的&#xff0c; 虽然在开发前我们已经敲定了功能列表和原型&#xff0c;但这两者只是一个框架&#xff0c;就好比要建一栋房子&#xff0…

前端 | (十四)canvas基本用法 | 尚硅谷前端HTML5教程(html5入门经典)

文章目录 &#x1f4da;canvas基本用法&#x1f407;什么是canvas(画布)&#x1f407;替换内容&#x1f407;canvas标签的两个属性&#x1f407;渲染上下文 &#x1f4da;绘制矩形&#x1f407;绘制矩形&#x1f407;strokeRect时&#xff0c;边框像素渲染问题&#x1f407;添加…

【Redis】String字符串类型-常用命令

文章目录 String字符串类型常用命令setgetMGETMSET 计数命令INCRINCRBYDECRDECRBYINCRBYFLOAT 其它命令APPENDGETRANGESETRANGESTRLEN 命令总结 String字符串类型 1&#xff09;⾸先Redis中所有的键的类型都是字符串类型 2&#xff09;字符串类型的值&#xff08;value&#…

鉴源实验室 | 自动驾驶传感器攻击研究

作者 | 付海涛 上海控安可信软件创新研究院汽车网络安全组 来源 | 鉴源实验室 社群 | 添加微信号“TICPShanghai”加入“上海控安51fusa安全社区” 01 自动驾驶汽车的脆弱性 自2015年以来&#xff0c;汽车的信息安全问题受到国内外的广泛关注。而随着汽车的智能化与网联化的…

C语言scanf()函数读取包含空格的字符串

scanf()函数读取输入时&#xff0c;遇到空格或者回车结束读取&#xff1b; 所以输入的字符串中有空格&#xff0c;到空格就中断了&#xff1b; 根据资料&#xff0c;使用 "%[^\n]" 这样的格式化符遇到空格继续读取&#xff0c;直到敲了回车&#xff1b; 看一下&am…

微信小程序UI自动化测试实践:Minium+PageObject

小程序架构上分为渲染层和逻辑层&#xff0c;尽管各平台的运行环境十分相似&#xff0c;但是还是有些许的区别&#xff08;如下图&#xff09;&#xff0c;比如说JavaScript 语法和 API 支持不一致&#xff0c;WXSS 渲染表现也有不同&#xff0c;所以不论是手工测试&#xff0c…

Javaweb之HTML,CSS的详细解析

2. HTML & CSS 1). 什么是HTML ? HTML: HyperText Markup Language&#xff0c;超文本标记语言。 超文本&#xff1a;超越了文本的限制&#xff0c;比普通文本更强大。除了文字信息&#xff0c;还可以定义图片、音频、视频等内容。 标记语言&#xff1a;由标签构成的语言…

Netty在Firbase中的使用

1.1前言 实时更新是现代应用程序中用户体验的一个组成部分。随着用户期待这样的行为&#xff0c;越来越多的应用程序都正在实时地向用户推送数据的变化。通过传统的3层架构很难实现实时的数据同步&#xff0c;其需要开发者管理他们自己的运维、服务器以及伸缩。通过维护到客户…

会声会影2024对比2023变化以及功能对比

全新会声会影2024版本现已登场&#xff0c;小伙伴们相信已经急不可待地想知道2024版到底有哪些新功能。对比2023版本&#xff0c;会声会影2024版本有没有功能的增强&#xff1f;事不宜迟&#xff0c;现在就让我们一起来看看会声会影2024对比2023的变化&#xff0c;包括功能对比…