Cpp::STL—list类的模拟实现(下)(14)

news2024/10/9 15:09:37

文章目录

  • 前言
  • 一、默认生成函数
    • 构造函数
    • 析构函数
  • 二、迭代器相关函数
    • begin & end
  • 三、访问容器相关函数
    • front & back
  • 四、插入、删除函数
    • insert
    • erase
    • push_back & pop_back
    • push_front & pop_front
  • 五、其余函数
    • size
    • resize
    • empty
    • swap
  • 总结


前言

  承接上文,我们现在来考虑一下list类的整体实现

  正文开始!


一、默认生成函数

构造函数

list是一个带头双向循环链表,在构造一个list对象时,直接申请一个头结点,并让其前驱指针和后继指针都指向自己即可
在这里插入图片描述

	// List的构造
	list()
	{
		CreateHead();
	}
private:
	void CreateHead()
	{
		_head = new Node;
		_head->_prev = _head;
		_head->_next = _head;
	}

如果是想直接插入n个T元素的话:

	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在这里必须写析构
	~list()
	{
		// clear函数用于清空容器,我们通过遍历的方式,逐个删除结点,只保留头结点即可
		clear();
		delete _head;
		_head = nullptr;
	}

	void clear()
	{
		Node* cur = _head->_next;
			
		// 采用头删除删除
		while (cur != _head)
		{
			_head->_next = cur->_next;
			delete cur;
			cur = _head->_next;
		}

		_head->_next = _head->_prev = _head;
	}

二、迭代器相关函数

	// 前面的的类就发挥作用了
	typedef ListIterator<T, T&, T*> iterator;
	typedef ListIterator<T, const T&, const T&> const_iterator;

begin & end

begin函数返回的是第一个有效数据的迭代器,end函数返回的是最后一个有效数据的下一个位置的迭代器

当然,还需要重载一对用于const对象的begin函数和end函数

对于list这个带头双向循环链表来说,其第一个有效数据的迭代器就是使用头结点后一个结点的地址构造出来的迭代器,而其最后一个有效数据的下一个位置的迭代器就是使用头结点的地址构造出来的迭代器。(最后一个结点的下一个结点就是头结点)

	//返回值没有传引用返回,由于该接口功能是返回开头和节点迭代器
	//如果传引用返回,会影响下一次调用该节点
	//而我们不需要拿到这个位置的迭代器,只需要拿到这个位置的信息
	iterator begin() 
	{ 
		return iterator(_head->_next); 
	}

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

	// const iterator可不可以呢?
	// 当然不行,我们不是想要iterator不可修改
	// 而是希望iterator指向的内容不可修改

	//iterator是一个类,如果在前面加const,它表示的意思是这个类对象本身不能被修改,而不是指向内容不能被修改
	//会导致++或–运算符之类的运算符重载会失效
	const_iterator begin() const 
	{ 
		return const_iterator(_head->_next); 
	}

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

三、访问容器相关函数

front & back

front和back函数分别用于获取第一个有效数据和最后一个有效数据,因此,实现front和back函数时,直接返回第一个有效数据和最后一个有效数据的引用即可

当然,这也需要重载一对用于const对象的front函数和back函数,因为const对象调用front和back函数后所得到的数据不能被修改

因为是双向链表,所以其实这是很方便的,但是要注意list是没有operator[ ]的

	// 直接使用list成员变量版本
	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;
	}

	// 使用迭代器来间接获取数据版本
	T& front()
	{
		return *begin(); 
	}
	
	T& back()
	{
		return *(--end()); 
	}
	
	const T& front() const
	{
		return *begin(); 
	}

	const T& back() const
	{
		return *(--end()); 
	}

四、插入、删除函数

别紧张,这对于双向链表来说是舒适区

insert

在这里插入图片描述
先根据所给迭代器得到该位置处的结点指针cur,然后通过cur指针找到前一个位置的结点指针prev,接着根据所给数据x构造一个待插入结点,之后再建立新结点与cur之间的双向关系,最后建立新结点与prev之间的双向关系即可

	// 在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);
	}

erase

erase函数可以删除所给迭代器位置的结点
在这里插入图片描述
先根据所给迭代器得到该位置处的结点指针cur,然后通过cur指针找到前一个位置的结点指针prev,以及后一个位置的结点指针next,紧接着释放cur结点,最后建立prev和next之间的双向关系即可

	// 删除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);
	}

push_back & pop_back

push_back和pop_back函数分别用于list的尾插和尾删,在已经实现了insert和erase函数的情况下,我们可以通过复用函数来实现push_back和pop_back函数

	void push_back(const T& val) 
	{ 
		insert(end(), val); 
	}

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

push_front & pop_front

当然,用于头插和头删的push_front和pop_front函数也可以复用insert和erase函数来实现,push_front函数就是在第一个有效结点前插入结点,而pop_front就是删除第一个有效结点

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

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

在vector实现push_back和pop_back时,通过begin和end得到迭代器指向的位置。返回变量为具有常性的临时变量,不能通过++或–对其修改(但是可以begin() + 3)
在List中迭代器可以进行++或–操作,由于不是对临时对象本身进行修改,而是在运算符重载中改变了运算符的行为,修改是临时对象指向的内容。在vector中修改是对象本身当然是不行的

五、其余函数

size

size函数用于获取当前容器当中的有效数据个数,因为list是链表,所以只能通过遍历的方式逐个统计有效数据的个数

		size_t size() const
		{
			Node* cur = _head->_next;
			size_t count = 0;
			while (cur != _head)
			{
				count++;
				cur = cur->_next;
			}

			return count;
		}

resize

  1. 若当前容器的size小于所给n,则尾插结点,直到size等于n为止
  2. 若当前容器的size大于所给n,则只保留前n个有效数据

因为size()要遍历一遍链表,这其实很不好,所以我们单独来个oldsize变量保存一下

	// 再次体现了复用思想
	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++;
			}
		}
	}

empty

empty函数用于判断容器是否为空,我们直接判断该容器的begin函数和end函数所返回的迭代器,是否是同一个位置的迭代器即可。(此时说明容器当中只有一个头结点)

	// 迭代器版本
	bool empty() const
	{
		return begin() == end(); 
	}

	// 直接使用list成员变量版本
	bool empty() const
	{
		return _head->_next == _head;
	}

swap

swap函数用于交换两个容器,list容器当中存储的实际上就只有链表的头指针,我们将这两个容器当中的头指针交换即可

	void swap(list<T>& l)
	{
		std::swap(_head, l._head);
	}

总结

  怎么样,是不是感觉很复杂呢!
  你可以自己去实现一下!

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

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

相关文章

数据恢复篇:适用于 Windows 操作系统的 5 大数据恢复软件

如今&#xff0c;数字空间正在快速发展。每个人都使用某种数字设备&#xff0c;如计算机、笔记本电脑、移动设备等来存储重要文档、照片、视频和其他重要文件。但事情并不总是一帆风顺。在很多情况下&#xff0c;技术会失败&#xff0c;您可能会遇到一些严重的问题&#xff0c;…

Leetcode 下一个排列

首先理解整数的字典序&#xff0c;字典序排列总是优先让“较小的”元素出现在前面。字典序的排列规则类似于字典中的单词排列方式&#xff0c;从左到右逐位比较&#xff0c;较小的数字优先出现。按照正整数元素排列的字典序&#xff0c;如果将每个排列视为一个整数值&#xff0…

Awaken Likho恶意组织利用高级网络工具对俄罗斯政府发起“猛攻”

近日&#xff0c;俄罗斯政府机构和工业实体遭遇了一场名为“ Awaken Likho ”的网络活动攻击活动。 卡巴斯基表示&#xff0c;攻击者现在更倾向于使用合法MeshCentral平台的代理&#xff0c;而不是他们之前用来获得系统远程访问权限的UltraVNC模块。这家俄罗斯网络安全公司详细…

函数信号发生器的使用方法

函数信号发生器&#xff08;Function Generator&#xff09;是电子工程师和技术人员在电路设计、测试和调试中常用的一种设备。它可以产生各种标准波形&#xff0c;如正弦波、方波、三角波等&#xff0c;以及用户自定义的任意波形。在本文中&#xff0c;我们将详细介绍函数信号…

Linux同时安装多个JDK

Linux同时安装多个JDK 一、JDK1.1、JDK的下载1.2、解压并放置目录 二、通过alias切换版本2.1、修改profile文件2.2、使用和验证 三、使用update-alternatives工具3.1、修改profile文件3.2、指定JDK版本3.3、使用和验证 四、总结 一、JDK 1.1、JDK的下载 JDK官网下载&#xff…

抖音外卖小时达服务商申请渠道开通,首选服务商推荐!

近日&#xff0c;国内头部短视频平台抖音正式宣布取消小时达业务的城市限制&#xff0c;并开启服务商的招募工作。随即&#xff0c;全国各地的商家和有意向进入本地生活赛道的创业者们都开始打听起了与抖音小时达服务商相关的各类消息&#xff0c;以抖音小时达服务商怎么申请为…

大模型生成PPT大纲优化方案:基于 nVidia NIM 平台的递归结构化生成

大模型生成PPT大纲优化方案&#xff1a;基于 nVidia NIM 平台的递归结构化生成 待解决的问题 生成PPT大纲是一种大模型在办公场景下应用的常见需求。 然而&#xff1a; 目前直接让大模型生成大纲往往是非结构化的&#xff0c;输出格式多样&#xff0c;难以统一和规范&#…

ZBrush入门使用介绍——17、FiberMesh

大家好&#xff0c;我是阿赵。   继续介绍ZBrush的使用。这次来看看FiberMesh功能。这是一个可以模仿毛发的功能。 一、 使用FiberMesh的预览功能 先准备一个模型&#xff0c;并生成多边形网格 然后按着Ctrl&#xff0c;在模型的表面画一个遮罩。 找到FiberMesh功能&#…

QT:计算点到线段的垂线段的距离

描述 在Qt中&#xff0c;要计算一个点到一条线段的垂线段的长度&#xff08;即点到线段上最近点的距离&#xff0c;且这个点是垂直于线段的&#xff09;&#xff0c;你不能直接使用QVector2D::distanceToLine&#xff0c;因为这个方法计算的是点到直线的垂直距离&#xff0c;而…

线程中的异常处理

线程中的异常处理 concurrent.futures — 启动并行任务 — Python 3.13.0 文档 from concurrent.futures import ThreadPoolExecutordef task():a 1 / 0print(任务执行中...)with ThreadPoolExecutor(max_workers5) as t:for i in range(10):worker t.submit(task)没有任任…

Unity实现自定义图集(二)

以下内容是根据Unity 2020.1.0f1版本进行编写的   实现一个自定义图集,该怎么入手呢。首先简单思考一下unity是怎么实现图集的。 因为unity的ui部分是开源的,所以我们可以看到UGUI的源代码,另外,Unity的内置Shader也是开源的,可以直接在官网下载(在下载的网页选择Built…

一文彻底搞懂大模型 - Hugging Face Transformers

Hugging Face Hugging Face Transformers是一个开源的预训练模型库&#xff0c;旨在将NLP领域的最新进展向更广泛的机器学习社区开放。该库包含了经过精心设计的最先进的Transformer架构&#xff0c;并提供了易于使用的API**&#xff0c;使得研究人员和开发者能够轻松地加载、…

Linux环境安装Anaconda

1.环境检查 uname -a2.下载Anaconda 下载地址: Anaconda 根据自己的版本下载 3.安装Anaconda 将下载的文件上传到Linux服务器设置权限 chmod x Anaconda3-2024.02-1-Linux-x86_64.sh执行.sh文件 ./Anaconda3-2024.02-1-Linux-x86_64.sh直接回车&#xff0c;一直回车&…

解决:由于没有远程桌面授权服务器可以提供许可证,远程会话连接已断开.请跟服务器管理员联系

今天早上远程连接京东云服务器&#xff0c;居然蹦出了下面的提示 查了下这个提示&#xff0c;出现这个报错的原因有两种&#xff1a; 1. 系统添加了“远程桌面会话主机”角色后&#xff0c;该授权到期&#xff08;可以免费试用120天&#xff0c;到期需要付费才能使用&#xff…

有哪些AI产品可以真正提高办公和学习效率?

你还在为加班熬夜、效率低下而苦恼吗&#xff1f;还在担心错过AI时代的风口&#xff0c;被时代抛弃吗&#xff1f; 告别效率焦虑&#xff0c;AI赋能你的学习和工作 现在有一些AI产品能为我们提供帮助&#xff0c;比如豆包、KIMI、通义千问、ChatGPT等等&#xff0c;帮助我们做P…

【深度学习】—激活函数、ReLU 函数、 Sigmoid 函数、Tanh 函数

【深度学习】—激活函数、ReLU 函数、 Sigmoid 函数、Tanh 函数 4.1.2 激活函数ReLU 函数参数化 ReLU Sigmoid 函数背景绘制 sigmoid 函数Sigmoid 函数的导数 Tanh 函数Tanh 函数的导数总结 4.1.2 激活函数 激活函数&#xff08;activation function&#xff09;用于计算加权和…

【有啥问啥】逆向工程(Reverse Engineering,RE):深度解析与技术方法

逆向工程&#xff08;Reverse Engineering&#xff0c;RE&#xff09;&#xff1a;深度解析与技术方法 引言 逆向工程&#xff08;Reverse Engineering&#xff0c;简称RE&#xff09;&#xff0c;作为现代科技领域中的一项重要技术&#xff0c;其影响力已远远超越了传统的硬…

“聪明车”上线“智慧路”!批量苏州金龙海格公交甘肃静宁投运

近日&#xff0c;甘肃省静宁县智慧公交系统正式上线&#xff0c;20台苏州金龙海格蔚蓝新能源公交车正式投入使用&#xff0c;大大提升了当地城市公共交通的智能水平以及城市形象。 智慧交通&#xff0c;势在必行 随着公共交通出行需求的不断升级&#xff0c;客运市场对于智能化…

爬虫请求响应以及提取数据

爬虫请求响应以及提取数据 回顾: 网页给客户端响应数据, 有哪些写法(在爬虫入门之爬虫原理以及请求响应这篇博客咯嘛有提到)? 1.响应对象.text(获取网页数据的时候会用到) 2.响应对象.content(将图片, 音频或视频等数据存放到文件的时候会用到) 那这一篇文章, 介绍一个新的写法…

小白打造爆款必备:速卖通平台测评与买家号培养全解析

随着亚马逊平台风控的持续升级&#xff0c;众多卖家面临着封店、侵权等诸多挑战&#xff0c;迫使许多商家开始探索其他市场&#xff0c;如速卖通、Temu等平台&#xff0c;这些平台对新入驻的卖家提供了显著的流量扶持。在速卖通平台的运营中&#xff0c;如何迅速且有效地提升店…