C++第十二弹 -- STL之list模拟实现

news2024/11/29 2:30:18

文章索引

  • 前言
  • 模拟实现list
    • 1. ListNode节点类
    • 2. list的迭代器封装
    • 3. 反向迭代器
    • 4. list类的模拟实现
    • 测试代码
  • list的反向迭代器
  • 总结

前言

通过模拟实现可以让我们更加深刻的理解C++底层STL的实现逻辑, 本篇就对list的底层进行模拟实现.

博客主页: 酷酷学!!! 点击关注 共同进步!!!


正文开始

模拟实现list

要模拟实现list, 必须要熟悉list的底层结构以及其接口的含义, 通过上一篇的学习, 这些内容基本掌握之后, 现在我们来模拟实现list.

1. ListNode节点类

#pragma once
#include<iostream>
using namespace std;
#include<assert.h>

namespace my
{
	//list的节点类
	template<class T>
	struct ListNode
	{

		ListNode(const T& val = T())
			: _prev(nullptr)
			, _next(nullptr)
			, _val(val)
		{}

		ListNode<T>* _prev;
		ListNode<T>* _next;
		T _val;
	};

2. list的迭代器封装

list的迭代器
迭代器有两种实现方式, 具体应根据容器底层数据结构的实现:
1.原生态指针, 比如:vector
2.将原生态指针进行封装, 因迭代器使用形式与指针完全相同,
因此在自定义的类中必须实现以下方法:
	1.指针可以解引用,迭代器的类中必须重载operator*()
	2.指针可以通过->访问其所指空间成员,迭代器类中必须重载operator->()
	3.指针可以++向后移动,迭代器类中必须重载operator++()与operator++(int)
	  至于operator--()/operator--(int)释放需要重载,根据具体的结构来抉择,
	  双向链表可以向前或者向后移动,所以需要重载,如果是forward_list就不需要重载--
	4.迭代器需要进行是否相等的比较,因此还需要重载operator==()与operator!=()
	template<class T,class Ref,class Ptr>
	class ListIterator
	{
		typedef ListNode<T> Node;
		typedef ListIterator<T, Ref, Ptr> Self;
		//Ref 和 Ptr类型需要重新定义下,实现反向迭代器时需要用到
	public:
		typedef Ref Ref;
		typedef Ptr Ptr;

		//构造
		ListIterator(Node* node = nullptr)
			: _node(node)
		{}

		//具有指针类似行为
		Ref operator*()
		{
			return _node->_val;
		}

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

		//迭代器支持移动
		Self& operator++()
		{
			_node = _node->_next;
			return *this;
		}

		Self operator++(int)
		{
			Self temp(*this);
			_node = _node->_next;
			return temp;
		}

		Self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}

		Self operator--(int)
		{
			Self temp(*this);
			_node = _node->_prev;
			return temp;
		}

		//迭代器支持比较
		bool operator!=(const Self& l) const
		{
			return _node != l._node;
		}

		bool operator==(const Self & l) const
		{
			return _node == l._node;
		}

		Node* _node;
	};

3. 反向迭代器

	//反向迭代器,迭代器复用
	template<class Iterator>
	class ReverseListIterator
	{
		//注意:此处typename的作用是明确的告诉编译器,Ref是Iterator类中的一个类型,
		//而不是静态成员变量,否则编译器编译时就不知道Ref是Iterator中的类型还是静态成员
		//变量,因为静态成员变量也是按照 类名::静态成员变量名 的方式访问的
	public:
		typedef typename Iterator::Ref Ref;
		typedef typename Iterator::Ptr Ptr;
		typedef ReverseListIterator<Iterator> Self;

		//构造
		ReverseListIterator(Iterator it)
			: _it(it)
		{}

		//具有指针类似行为
		Ref operator*()
		{
			Iterator temp(_it);
			--temp;
			return *temp;
		}

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

		//迭代器支持移动
		Self& operator++()
		{
			--_it;
			return *this;
		}

		Self& operator++(int)
		{
			Self temp(*this);
			--_it;
			return *this;
		}

		Self& operator--()
		{
			++_it;
			return *this;
		}

		Self& operator--(int)
		{
			Self temp(*this);
			++_it;
			return temp;
		}

		//迭代器支持比较
		bool operator!=(const Self& l) const
		{
			return _it != l._it;
		}

		bool operator==(const Self& l) const
		{
			return _it == l._it;
		}
		Iterator _it;
	};

4. list类的模拟实现

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

	public:

		//正向迭代器
		typedef ListIterator<T, T&, T*> iterator;
		typedef ListIterator<T, const T&, T*> const_iterator;

		//反向迭代器
		typedef ReverseListIterator<iterator> reverse_iterator;
		typedef ReverseListIterator<const_iterator> const_reverse_iterator;
		
		//List的构造
		list()
		{
			CreateHead();
		}

		list(int n, const T& value = T())
		{
			CreateHead();
			for (int i = 0; i < n; ++i)
			{
				push_back(value);
			}
		}

		template<class Iterator>
		list(Iterator first, Iterator last)
		{
			CreateHead();
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}
		
		list(const list<T>& l)
		{
			CreateHead();
			//用l中的元素构造临时的temp,然后与当前对象交换
			list<T> temp(l.begin(), l.end());
			this->swap(temp);
		}

		list<T>& operator=(list<T> l)
		{
			this->swap(l);
			return *this;
		}

		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}


		//list的迭代器
		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);
		}

		reverse_iterator rbegin()
		{
			return reverse_iterator(end());
		}

		reverse_iterator rend()
		{
			return reverse_iterator(begin());
		}

		const_reverse_iterator rbegin() const
		{
			return const_reverse_iterator(end());
		}

		const_reverse_iterator rend() const
		{
			return const_reverse_iterator(begin());
		}

		//List的容器相关
		size_t size() const
		{
			Node* cur = _head->_next;
			size_t count = 0;
			while (cur != _head)
			{
				++count;
				cur = cur->_next;
			}
			return count;
		}

		bool empty() const
		{
			return _head->_next = _head;
		}

		void resize(size_t newsize, const T* data = T())
		{
			size_t oldsize = size();
			if (newsize <= oldsize)
			{
				//将有效元素减少到newsize
				while (newsize < oldsize)
				{
					pop_back();
					--oldsize;
				}
			}
			else
			{
				while (oldsize < newsize)
				{
					push_back(data);
					++oldsize;
				}
			}
		}

		//list的元素访问操作
		//注意:List不支持operator[]
		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;
		}


		//list的插入和删除
		void push_back(const T& val)
		{
			insert(end(), val);
		}

		void pop_back()
		{
			erase(--end());
		}

		void push_front(const T& val)
		{
			insert(begin(), val);
		}

		void pop_front()
		{
			erase(begin());
		}

		//在pos位置前插入值为val的节点
		iterator insert(iterator pos, const T& val)
		{
			Node* pNewNode = new Node(val);
			Node* pCur = pos._node;
			//先将新节点插入
			pNewNode->_prev = pCur->_prev;
			pNewNode->_next = pCur;
			pNewNode->_prev->_next = pNewNode;
			pCur->_prev = pNewNode;
			return iterator(pNewNode);
		}

		//删除pos位置的节点,返回该节点的下一个位置
		iterator erase(iterator pos)
		{
			//找到待删除的节点
			Node* pDel = pos._node;
			Node* pRet = pDel->_next;

			//将该节点从链表中拆下来并删除
			pDel->_prev->_next = pDel->_next;
			pDel->_next->_prev = pDel->_prev;
			delete pDel;

			return iterator(pRet);
		}

		void clear()
		{
			Node* cur = _head->_next;
			//采用头删除删除
			while (cur != _head)
			{
				Node* next = cur->_next;
				delete cur;
				cur = next;
			}
			_head->_next = _head->_prev = _head;
		}
		
		void swap(my::list<T>& l)
		{
			std::swap(_head, l._head);
		}

	private:
		void CreateHead()
		{
			_head = new Node;
			_head->_prev = _head;
			_head->_next = _head;
		}
		Node* _head;
	};	
}

测试代码

对构造函数进行测试

//对模拟实现的list进行测试
//正向打印链表
template<class T>
void PrintList(const my::list<T>& l)
{
	auto it = l.begin();
	while (it != l.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

//测试list的构造
void Testmylist1()
{
	my::list<int> l1;
	my::list<int> l2(10, 5);
	PrintList(l2);

	int array[] = { 1,2,3,4,5,6,7,8,9,0 };
	my::list<int> l3(array, array + sizeof(array) / sizeof(array[0]));
	PrintList(l3);

	my::list<int> l4(l3);
	PrintList(l4);

	l1 = l4;
	PrintList(l1);
}

在这里插入图片描述

对头/尾插入删除进行测试

//PushBack()/PopBack()/PushFront()/PopFront()
void Testmylist2()
{
	//测试pushBack和PopBack
	my::list<int> l;
	l.push_back(1);
	l.push_back(2);
	l.push_back(3);
	PrintList(l);

	l.pop_back();
	l.pop_back();
	PrintList(l);

	l.pop_back();
	cout << l.size() << endl;

	//测试pushFront与popFront
	l.push_front(1);
	l.push_front(2);
	l.push_front(3);
	PrintList(l);

	l.pop_front();
	l.pop_front();
	PrintList(l);

	l.pop_front();
	cout << l.size() << endl;
}

在这里插入图片描述

测试指定位置插入删除

//测试insert和erase
void Testmylist3()
{
	int array[] = { 1,2,3,4,5 };
	my::list<int> l(array, array + sizeof(array) / sizeof(array[0]));
	auto pos = l.begin();
	l.insert(l.begin(), 0);
	PrintList(l);

	++pos;
	l.insert(pos, 2);
	PrintList(l);

	l.erase(l.begin());
	l.erase(pos);
	PrintList(l);

	//pos指向的节点已经被删除,pos迭代器失效
	cout << *pos << endl;
	auto it = l.begin();
	while (it != l.end())
	{
		it = l.erase(it);
	}
	cout << l.size() << endl;
}

在这里插入图片描述

测试反向迭代器

//测试反向迭代器
void Testmylist4()
{
	int array[] = { 1,2,3,4,5 };
	my::list<int> l(array, array + sizeof(array) / sizeof(array[0]));
	
	auto rit = l.rbegin();
	while (rit != l.rend())
	{
		cout << *rit << " ";
		++rit;
	}
	cout << endl;

	const my::list<int> cl(l);
	auto crit = l.rbegin();
	while (crit != l.rend())
	{
		cout << *crit <<" ";
		++crit;
	}
	cout << endl;
}

在这里插入图片描述

list的反向迭代器

我们知道, 反向迭代器的++就是正向迭代器的- -, 反向迭代器的- -就是正向迭代器的++, 因此反向迭代器的实现可以借助正向迭代器, 即: 返现迭代器内部可以包含一个正向迭代器, 对正向迭代器的接口进行包装即可.

	//反向迭代器,迭代器复用
	template<class Iterator>
	class ReverseListIterator
	{
		//注意:此处typename的作用是明确的告诉编译器,Ref是Iterator类中的一个类型,
		//而不是静态成员变量,否则编译器编译时就不知道Ref是Iterator中的类型还是静态成员
		//变量,因为静态成员变量也是按照 类名::静态成员变量名 的方式访问的
	public:
		typedef typename Iterator::Ref Ref;
		typedef typename Iterator::Ptr Ptr;
		typedef ReverseListIterator<Iterator> Self;

		//构造
		ReverseListIterator(Iterator it)
			: _it(it)
		{}

		//具有指针类似行为
		Ref operator*()
		{
			Iterator temp(_it);
			--temp;
			return *temp;
		}

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

		//迭代器支持移动
		Self& operator++()
		{
			--_it;
			return *this;
		}

		Self& operator++(int)
		{
			Self temp(*this);
			--_it;
			return *this;
		}

		Self& operator--()
		{
			++_it;
			return *this;
		}

		Self& operator--(int)
		{
			Self temp(*this);
			++_it;
			return temp;
		}

		//迭代器支持比较
		bool operator!=(const Self& l) const
		{
			return _it != l._it;
		}

		bool operator==(const Self& l) const
		{
			return _it == l._it;
		}
		Iterator _it;
	};

总结

通过以上的实现,可以模拟出一个类似于list的数据结构,并且可以对其中的元素进行添加、删除、查找、等操作。这样就可以在不使用C++内置的list时,使用自己实现的List类来进行相同的操作。

感谢您的点赞与收藏!!!

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

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

相关文章

影响五金精密零件加工价格的因素

在制造业中&#xff0c;五金精密零件的加工价格受到多种因素的影响。了解这些因素&#xff0c;对于企业合理控制成本、选择合适的加工供应商至关重要。 首先&#xff0c;零件的设计复杂度是一个重要因素。复杂的设计通常需要更先进的加工技术和更多的加工工序。例如&#xff0c…

intel ECI作为ACRN VM使用dpdk(vfio和iommu问题)以及img扩容

ACRN虚拟机内IOMMU 对非虚拟机而言&#xff0c;只要在BIOS里开启VT-d就可以用iommu去映射vfio使用DPDK&#xff0c;但是在虚拟机中即便BIOS开启了VT-d&#xff0c;它也传不到VM中。因此这个帖子解决一下这个问题。 在ACRN的launch脚本中需要passthru两个网卡&#xff0c;一个用…

C++ TinyWebServer项目总结(8. 高性能服务器程序框架)

《Linux 高性能服务器编程》一书中&#xff0c;把这一章节作为全书的核心&#xff0c;同时作为后续章节的总览。这也意味着我们在经历了前置知识的学习后&#xff0c;正式进入了 Web 服务器项目的核心部分&#xff01; 前置内容回顾&#xff1a; 1. C TinyWebServer项目总结&…

《深入浅出多模态》(九)多模态经典模型:MiniGPT-v2、MiniGPT5

🎉AI学习星球推荐: GoAI的学习社区 知识星球是一个致力于提供《机器学习 | 深度学习 | CV | NLP | 大模型 | 多模态 | AIGC 》各个最新AI方向综述、论文等成体系的学习资料,配有全面而有深度的专栏内容,包括不限于 前沿论文解读、资料共享、行业最新动态以、实践教程、求职…

【Datawhale AI夏令营第四期】 浪潮源大模型应用开发方向笔记 Task03 大咖项目分享 人话八股文Bakwaan_Buddy项目开发尝试

【Datawhale AI夏令营第四期】 浪潮源大模型应用开发方向笔记 Task03 人话八股文Bakwaan_Buddy项目开发尝试 Idea: 我们草台班子目前的想法是解决大家计算机学院毕业面临的BUG——不爱背、背不下来八股文&#xff0c;觉得枯燥、烦、工作了用不着&#xff0c;反正就是知识他不…

triton之flaggems的point-wise分析

一 流程 1 代码分析 1.1 parameter_ref_for_wrapper 对应 二 拾遗 报错 1 缺少@triton.jit File "/opt/conda/envs/py38/lib/python3.8/site-packages/Open_triton/utils/dynamic_index_put.py", line 676, in decorator num_inputs = len(fn.arg_name…

提高谷歌引擎搜索排名一连串儿丝滑小连招

在运营独立网站时&#xff0c;最令人沮丧的事情莫过于网站未能获得谷歌的高排名&#xff0c;尽管付出了诸多努力。由于互联网上内容繁多&#xff0c;找到在谷歌上获得排名的确切方法几乎是不可能的。谷歌有多个排名因素&#xff0c;但这些因素并非孤立存在&#xff0c;而是由多…

java如何判断一个列表中是否存在大于1000的数字

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

帆软报表设计器函数相关问题

官方漏洞声明&#xff1a;安全漏洞声明- FineReport帮助文档 - 全面的报表使用教程和学习资料 最近出的两个漏洞&#xff0c;官方已修复&#xff0c;问题有些相似&#xff0c;都是通过设计器函数来构造rce。尤其第二个sql注入造成RCE的漏洞还是挺有意思的&#xff0c;记录一下…

地表最强Ai视频创作工具?Pika保姆级注册使用指南

大家好&#xff01;我是YUAN。 今天&#xff0c;我要带大家探索一个全新的领域——AI视频生成。Pika Labs以其独特的魅力和强大的功能&#xff0c;成为了众多创作者的新宠。准备好了吗&#xff1f;让我们一起探索Pika Labs的无限可能。 一、Pika Labs是什么&#xff1f; Pika …

fl studio24.1.1中文完整版,直接安装激活!免费分享

&#x1f389; FL Studio 24.1.1 中文版本&#xff0c;音乐制作人的福音&#xff01; 嗨&#xff0c;亲爱的音乐制作爱好者们&#xff01;&#x1f44b; 如果你对音乐创作有着无限的热爱&#xff0c;那你绝对不能错过 FL Studio 24.1.1 中文版本这款神器。它不仅是一个音乐制作…

跟着 iLogtail 学习高质量软件建设

作者&#xff1a;余韬 本文根据 iLogtail PMC 成员余韬 2024 年 6 月 26 日在 DBAPlus 社群的公开直播《云上千万级可观测 Agent SRE 实践》整理而成。 引言 近年来&#xff0c;关于可靠性工程这一话题的热议不断升温&#xff0c;这主要归因于当前形势的显著变化。 首先&…

如何备份电脑所有数据?四个方法实现一键备份所有数据

备份电脑所有数据是一个重要的步骤&#xff0c;可以确保在数据丢失或损坏时能够迅速恢复。以下是一些备份电脑所有数据的方法&#xff0c;对于有重要数据的企业来说非常实用。 一、使用外置存储设备 选择设备&#xff1a;首先&#xff0c;选择一个容量足够大的外置存储设备&am…

肿瘤免疫治疗队列转录组及单细胞数据下载-TIGER

目录 转录组数据 ​编辑单细胞数据 TIGER不仅提供了多种在线分析&#xff0c;还提供了多个肿瘤免疫治疗队列转录组及单细胞数据下载。 TIGER:肿瘤免疫治疗&#xff08;转录组单细胞免疫&#xff09;_肿瘤免疫治疗基因表达资源tiger-CSDN博客 转录组数据 TIGER: Tumor Immu…

构建全面的用户增长蓝图

在竞争日益激烈的SaaS市场中&#xff0c;用户增长已成为企业持续繁荣的关键驱动力。为了实现这一目标&#xff0c;企业不仅需要制定高效的用户增长策略&#xff0c;还需要借助先进的工具和技术来加速这一过程。 1. 明确目标市场与用户画像 首先&#xff0c;SaaS企业需要深入了…

PNG转BMP要怎么操作?分享四种不同的转换方案

PNG转BMP要怎么操作&#xff1f;PNG和BMP是两种常见的图像格式&#xff0c;PNG格式以其无损压缩和支持透明度而闻名&#xff0c;而BMP格式则是一种未经压缩的位图格式&#xff0c;常用于操作系统和应用程序内部。有时出于兼容性或特定需求的原因&#xff0c;你可能需要将 PNG 文…

【全网最真实测评】随身WiFi值得入手吗?自费入手华为、中兴、格行、上赞4款随身WiFi,内含国产4款热门随身WiFi推荐!(最实用、最高性价比!)

随身WiFi的风越吹越大&#xff0c;市场乱象也更变本加厉。作为一名资深随身WiFi使用者&#xff0c;接触过太多的随身WiFi产品&#xff0c;越是了解这个行业黑幕&#xff0c;就越对无良商家夸大宣传、虚标限速&#xff0c;甚至售卖二手产品的行为深恶痛绝&#xff01; 本篇测评涉…

搭建高可用的微信小程序服务(Alibaba Cloud Linux 3)

本文介绍如何在阿里云云服务器ECS上基于Alibaba Cloud Linux 3操作系统搭建高可用的微信小程序服务端&#xff0c;并在本地开发一个名为ECS小助手的简单微信小程序。通过远程调用部署在ECS上的服务端&#xff0c;实现在小程序中输入框输入ECS实例ID查询实例详细信息的功能。 步…

Java使用Map+函数式接口实现策略模式

一、项目背景 在项目中&#xff0c;我们处理了各种类型的通知消息。在没有采用策略模式之前&#xff0c;代码中充斥了大量的 if-else 语句&#xff0c;这不仅让整个项目显得杂乱无章&#xff0c;还增加了后续维护的难度。为了解决这一问题&#xff0c;我们采用了 Map 和函数式…

Vue事件处理:v-on 指令

1、v-on 指令 在 Vue.js 中&#xff0c;事件处理是一个很重要的环节&#xff0c;可以使用 v-on 指令对 DOM 事件进行监听。该指令通常在模板中直接使用&#xff0c;在触发事件时执行相应的 JavaScript 代码。在 HTML 元素中使用 v-on 指令时&#xff0c;v-on 后面可以是所有的…