C++ 【从零手撕,模拟实现list类】

news2025/3/13 15:19:02

1、基础框架

        从list源代码中我们可以看到list底层为带哨兵位双向循环链表,从设计者的角度来看,我们并不知道用户在链表中存储的数据类型,所以在这里要使用类模板。再者,每一个节点都包含了前向、后向指针和存储的数据值,所以在这里也可以将每一个节点封装成一个类,结点中的数据必须要在链表中能被访问,所以在设计结点的类时,类中的成员属性应该能被外部访问,即节点类内部应当使用相应的访问限定符进行修饰,也可以直接将class换成struct(struct默认访问限定符为public)

        在书写代码之前我们应该理清两个类之间的关系:

  • list:list为链表对象,成员属性为_head,类型为: 节点*。
  • _head:指向哨兵位头节点的指针,节点内部存放的数据类型未知。初始化时_prev指向_head,_next指向_head。

        那么代码实现就可以写成:

#pragma once
#include <iostream>

namespace ltq
{
	template<class T>
	struct list_node
	{
		typedef list_node<T> Node;//节点模板实例化

		Node* _next;
		Node* _prev;
		T _val;

		list_node()
			:_next(nullptr)//新节点初始化成空指针
			,_prev(nullptr)
			,_val(T())
		{}
	};

	template<class T>
	class list
	{
		typedef list_node<T> Node;//节点模板实例化
	public:
		list()
			:_head(new Node)//创建一个头节点
		{
			_head->_prev = _head;
			_head->_next = _head;
		}

	private:
		Node* _head;
	};
}

2、push_back实现

        双向带头循环链表的尾部插入是比较简单的,首先需要找到目前链表的尾节点,再创建新的节点并按要插入的数据要求对新节点进行初始化。初始化结束之后进行相应的链接就可以了。

		void push_back(const T& val)
		{
			Node* tail = _head->_prev; //找到尾节点
			Node* newNode = new Node(val);//调用构造函数创建节点对象

			//进行链接
			newNode->_prev = tail;
			tail->_next = newNode;
			newNode->_next = _head;
			_head->_prev = newNode;
		}

        这里需要对list_node的构造函数进行修改,因为再创建节点的过程中,我们需要给定存储数据的具体数值,所以,在前面的list_node构造函数中给上缺省值。

		list_node(const T& val = T())//给缺省值
			:_next(nullptr)
			,_prev(nullptr)
			,_val(val)//缺省值或者参数进行初始化数据
		{}

         通过节点之间的连接,链表的逻辑结构就变成了下面的形式:

        数据能否正常插入呢?运行代码没有出现错误信息,为了能够方便的验证,下面就先实现链表的迭代器。

3、迭代器实现

        链表的迭代器要向string、vector一样的连续结构的迭代器一样去访问链表中的数据是值得思考的。连续的数据结构直接可以通过指针来实现迭代器,因为指针解引用就可以顺利的拿到该位置的数据,指针的加减操作就能轻松的实现目标位置的移动。然而,对于链表这种物理结构不连续的数据结构来说想达到像顺序结构一样的功能,显然就不能仅使用指针来进行实现。

        ps:假设依然采用指针直接实现迭代器,那么对指针解引用是不是我们想要的节点内部的数据呢?显然,此时的解引用只是这个节点本身。并且此时倘若指针做加减操作,具体指向哪里,我们也不得而知。

        综上,我们要实现和顺序结构功能相同的链表迭代器,就必须对迭代器进行封装,在迭代器内部使用操作符重载来实现预期的功能。这里的迭代器仍然是指向节点的指针,我们需要重载解引用操作符、以及其他操作符以实现正常功能。

        显然,这里我们需要使用类模板来实现迭代器的功能描述。由于在list内部要使用迭代器的成员属性,这里直接就将其定义为struct类方便后续的操作。

	template<class T>
	struct __list_iterator
	{
		typedef list_node<T> Node;

		Node* _node;
	};

        参考源码中的实现,迭代器的begin()返回哨兵位的下一个节点的指针,end()返回的是哨兵位节点的指针 。那么代码就可以写成:

        typedef __list_iterator<T> iterator;

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

		iterator end()
		{
			return _head;
		}

         在定义时我们知道迭代器就是节点的指针,那么迭代器的构造函数就可以写成如下的形式:

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

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

        这里会有一个疑问,begin()中返回的是_head->_next,它的类型为Node*,而迭代器尾__list_node<T>,这里其实发生了隐式类型转换。也就是单参数的构造函数执行隐式类型转换。

3.1、迭代器运算符重载

        如上所述,我们首先需要对解引用操作符进行重载,我们期望对节点的迭代器解引用之后得到节点内的数据。数据类型为T,且函数调用结束之后_val 不会消失,故直接返回T引用。

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

        前置++和后置++的重载。这里需要明确的是,为了符合规范,迭代器的加减操作之后返回的数据都是迭代器类型。

		__list_iterator<T> operator++()
		{
			_node = _node->_next;
			return *this;
		}
        
        //后置++
		__list_iterator<T> operator++(int)
		{
            //调用拷贝构造 创建中间迭代器对象
			__list_iterator<T> tmp(_node);
			_node = _node->_next;
			return tmp;
		}

         在目前的基础上再重载一下!=符号,就可以实现对基本的迭代器的使用。

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

        测试一下上面的功能是否正常:

         3.2、const对象的迭代器

        如果按照顺序结构迭代器的套路来写,就会产生一个错误,像下面这样:

		//以前的套路
		typedef const __list_iterator<T> const_iterator;
		const_iterator begin()const
		{
			return _head->_next;
		}

		const_iterator end()const
		{
			return _head;
		}

        返回值将会是一个const修饰的迭代器,更简单的说就是一个const 修饰的节点指针。那么就有大麻烦了!这个指针将不能移动!!!前面的++ 操作符压根没有办法使用。这时我们需要回头来思考一下,const对象的迭代器不期望的是修改节点内部的值,但是可以遍历访问。所以我们只需要做的操作就是再返回值T&那里 加上const修饰,那么返回的值将不能被修改。

        由于const链表对象和普通的链表对象只是在这部分有差异,所以为了减少代码的冗余度,这里需要增加类模板的参数以实现代码的表面上的复用(对编译器来说这是两个类!并不是复用)。

    template<class T,class Ref>
		typedef __list_iterator<T, const T&> const_iterator;
		const_iterator begin()const
		{
			return _head->_next;
		}

		const_iterator end()const
		{
			return _head;
		}

4、insert实现

        链表的pos位置插入是很容易实现的,库中一般规定在pos位置的前面进行插入。并且返回新插入节点的迭代器。

		iterator insert(iterator pos,const T& val)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* newNode = new Node(val);

			prev->_next = newNode;
			newNode->_prev = prev;

			newNode->_next = cur;
			cur->_prev = newNode;

			return newNode;
		}

5、erase实现

        参考库中的形式,函数返回删除节点之后的节点的迭代器。

		iterator erase(iterator pos)
		{
			Node* cur = pos._node;
			Node* next = cur->_next;
			Node* prev = cur->_prev;

			prev->_next = next;
			next->_prev = prev;
			delete cur;

			return next;
		}

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

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

相关文章

gesp(C++五级)(1)洛谷:B3941:[GESP样题 五级] 小杨的锻炼

gesp(C五级)&#xff08;1&#xff09;洛谷&#xff1a;B3941&#xff1a;[GESP样题 五级] 小杨的锻炼 题目描述 小杨的班级里共有 n n n 名同学&#xff0c;每位同学都有各自的锻炼习惯。具体来说&#xff0c;第 i i i 位同学每隔 a i a_i ai​ 天就会进行一次锻炼&#x…

【k8s】scc权限 restricted、anyuid、privileged

文章目录 概述1. 内置的scc2. OpenShift如何确定pod的scc2.1 Pod未带SCC标签的情况2.2. Pod带有SCC标签的情况 参考 概述 在OpenShift&#xff08;后文简称OCP&#xff09;中&#xff0c;很早就一个概念&#xff1a;Security Context Constraints &#xff0c;简称SCC&#xf…

高速光电探测器设计 PIN APD铟镓砷TIA放大脉冲误码测试800-1700nm

高速光电探测器PIN APD铟镓砷TIA放大脉冲误码测试800-1700nm &#xff08;对标:索雷博APD431A&#xff09; &#xff08;对标:索雷博APD431A&#xff09; &#xff08;对标:索雷博APD431A&#xff09; 规格参数: 波长范围:800-1700nm 输出带宽:DC-400MHz&#xff08;-3dB&…

配置 One API + ChatGPT-Next-Web,以讯飞星火认知大模型为例

配置 One API ChatGPT-Next-Web&#xff0c;以讯飞星火认知大模型为例 1.0 One API 配置1.1 获取大模型 API1.2 配置 OneAPI 渠道1.3 配置OneAPI 令牌 2.0 ChatGPT-Next-Web 配置 同步发布在个人笔记配置 One API ChatGPT-Next-Web&#xff0c;以讯飞星火认知大模型为例 上一…

初学者如何保护WordPress网站内容

在如今的数字时代&#xff0c;保护你的WordPress网站内容变得尤为重要&#xff0c;尤其是当你刚刚开始运营自己的网站时。盗版和内容窃取不仅可能影响你的网站流量&#xff0c;还可能损害你的声誉。这篇文章将为初学者介绍几种简单的方法&#xff0c;帮助你有效保护WordPress内…

Maven核心插件之maven-resources-plugin

前言 Maven 插件是 Maven 构建系统的重要组成部分&#xff0c;它们为 Maven 提供了丰富的功能和扩展能力&#xff0c;使得 Maven 不仅是一个构建工具&#xff0c;更是一个强大的项目管理平台。在 Maven 项目中&#xff0c;插件的使用通常通过配置 pom.xml 文件来完成。每个插件…

ASP.NET Core - IStartupFilter 与 IHostingStartup

ASP.NET Core - IStartupFilter 与 IHostingStartup 1. IStartupFilter2 IHostingStartup2.5.1 创建外部程序集2.5.2 激活外部程序集 1. IStartupFilter 上面讲到的方式虽然能够根据不同环境将Startup中的启动逻辑进行分离&#xff0c;但是有些时候我们还会可以根据应用中的功能…

SOME/IP 协议详解——服务发现

文章目录 1. Introduction &#xff08;引言&#xff09;2. SOME/IP Service Discovery (SOME/IP-SD)2.1 General&#xff08;概述)2.2 SOME/IP-SD Message Format2.2.1 通用要求2.2.2 SOME/IP-SD Header2.2.3 Entry Format2.2.4 Options Format2.2.4.1 配置选项&#xff08;Co…

【大数据】机器学习-----最开始的引路

以下是关于机器学习的一些基本信息&#xff0c;包括基本术语、假设空间、归纳偏好、发展历程、应用现状和代码示例&#xff1a; 一、基本术语 样本&#xff08;Sample&#xff09;&#xff1a; 也称为实例&#xff08;Instance&#xff09;或数据点&#xff08;Data Point&…

基于物联网技术的污水处理厂解决方案

随着城市化进程的加快&#xff0c;污水处理成为保障城市生态环境的重要环节。传统污水处理方式存在效率低下、管理复杂、能耗高等问题。为了提升污水处理效率与管理水平&#xff0c;天拓四方提出基于物联网技术的污水处理厂解决方案&#xff0c;旨在实现污水处理过程的可视化、…

《分布式光纤传感:架设于桥梁监测领域的 “智慧光网” 》

桥梁作为交通基础设施的重要组成部分&#xff0c;其结构健康状况直接关系到交通运输的安全和畅通。随着桥梁建设规模的不断扩大和服役年限的增长&#xff0c;桥梁结构的安全隐患日益凸显&#xff0c;传统的监测方法已难以满足对桥梁结构健康实时、全面、准确监测的需求。分布式…

React中ElementFiber对象、WorkInProgress双缓存、ReconcileRenderCommit、第一次挂载过程详解

基础概念 Element对象与Fiber对象 Element对象与Fiber对象 Element 对象 定义 React 的 Element 对象是一个描述用户界面&#xff08;UI&#xff09;的普通 JavaScript 对象&#xff0c;通常由 React.createElement 或 JSX 语法生成。 作用 它是 React 应用中的一种描述 …

excel仅复制可见单元格,仅复制筛选后内容

背景 我们经常需要将内容分给不同的人&#xff0c;做完后需要合并 遇到情况如下 那是因为直接选择了整列&#xff0c;当然不可以了。 下面提供几种方法&#xff0c;应该都可以 直接选中要复制区域然后复制&#xff0c;不要选中最上面的列alt;选中可见单元格正常复制&#xff…

在 Linux 下Ubuntu创建同权限用户

我是因为不小心把最开始创建的用户的文件夹颜色搞没了&#xff0c;再后来全白用习惯了&#xff0c;就不想卸载了&#xff0c;像创建一个和最开始创建的用户有一样的权限可以执行sudo -i进入root一样的用户 如图这是最原始的样子 第一步 创建新用户&#xff0c;我这里是因为之前…

Edge浏览器内置的截长图功能

Edge浏览器内置截图功能 近年来&#xff0c;Edge浏览器不断更新和完善&#xff0c;也提供了长截图功能。在Edge中&#xff0c;只需点击右上角的“...”&#xff0c;然后选择“网页捕获”->“捕获整页”&#xff0c;即可实现长截图。这一功能的简单易用&#xff0c;使其成为…

aws(学习笔记第二十三课) step functions进行开发(lambda函数调用)

aws(学习笔记第二十三课) 开发step functions状态机的应用程序 学习内容&#xff1a; step functions状态机的概念开发简单的step functions状态机 1. step functions状态机概念 官方说明文档和实例程序 AWS的官方给出了学习的链接和实例程序。使用SAM创建step functions 借…

《拉依达的嵌入式\驱动面试宝典》—操作系统篇(二)

《拉依达的嵌入式\驱动面试宝典》—操作系统篇(二) 你好,我是拉依达。 感谢所有阅读关注我的同学支持,目前博客累计阅读 27w,关注1.5w人。其中博客《最全Linux驱动开发全流程详细解析(持续更新)-CSDN博客》已经是 Linux驱动 相关内容搜索的推荐首位,感谢大家支持。 《拉…

浅谈云计算07 | 云安全机制

浅谈云计算安全机制&#xff1a;全方位守护云端世界 一、引言二、加密技术&#xff1a;数据的隐形护盾三、散列机制&#xff1a;数据完整性的忠诚卫士四、数字签名&#xff1a;数据来源与真伪的鉴定专家五、公钥基础设施&#xff08;PKI&#xff09;&#xff1a;信任的基石六、…

Vulnhub DC-9靶机实战

前言 这里说一下这个靶机的难点 1.这次sql注入是两个库的&#xff0c;在不使用sqlmap的情况下很多人直接database()看数据库&#xff0c;另一个库反倒没关注 2.nmap的扫描方式如果用-sT的tcp连接扫端口的话是扫不到那些被防火墙过滤的端口的&#xff0c;直接nmap ip就可以 3…

语音技术与人工智能:智能语音交互的多场景应用探索

引言 近年来&#xff0c;智能语音技术取得了飞速发展&#xff0c;逐渐渗透到日常生活和各行各业中。从语音助手到智能家居控制&#xff0c;再到企业客服和教育辅导&#xff0c;语音交互正以前所未有的速度改变着人机沟通的方式。这一变革背后&#xff0c;人工智能技术无疑是关键…