探索C嘎嘎的奇妙世界:第十九关---STL(list的模拟实现)

1. 基本框架

首先,我们先从节点的准备工作入手,请看示例:        

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
//节点
template<class T>
struct ListNode
{
	ListNode<T>* _next;
	ListNode<T>* _prev;
	T _data;

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

在上述代码中:先定义一个双向链表的节点结构ListNode。节点结构ListNode包含以下成员变量:
        1. _next:指向下一个节点的指针。
        2. _prev:指向上一个节点的指针。
        3. _data:存储节点数据的变量。

        节点结构ListNode还包含一个构造函数,用于初始化成员变量。构造函数接受一个参数data,用于初始化_data成员变量。如果没有提供参数,则_data成员变量的默认值为T(),即T类型的默认构造函数的返回值。

        该头文件还使用了iostream和assert.h两个标准头文件,并使用了std命名空间。

2. 封装迭代器

请看示例代码:

	//迭代器
	template<class T, class Ref, class Ptr>
	struct ListIterator
	{
		typedef ListNode<T> Node;
		typedef ListIterator<T, Ref, Ptr> Self;
		Node* _node;

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

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

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

		Self operator++(int)
		{
			Self tmp(*this);
			_node = _node->_next;

			return tmp;
		}

		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)
		{
			return _node != it._node;
		}

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

在上述代码中:定义了一个迭代器结构ListIterator,用于遍历双向链表。迭代器结构ListIterator包含以下成员变量和成员函数:
成员变量: _node:指向当前迭代器所指向的节点的指针。

成员函数如下:
        1. 构造函数:接受一个参数node,用于初始化迭代器的节点指针。
        2. 前置递增运算符(++it):将迭代器指向下一个节点,并返回自身的引用。
        3. 前置递减运算符(--it):将迭代器指向上一个节点,并返回自身的引用。
        4. 后置递增运算符(it++):将迭代器指向下一个节点,并返回之前的迭代器的副本。
        5. 后置递减运算符(it--):将迭代器指向上一个节点,并返回之前的迭代器的副本。
        6. 解引用运算符(*):返回迭代器指向节点的数据的引用。
        7. 成员访问运算符(->):返回迭代器指向节点数据的指针。
        8. 不等于运算符(!=):判断当前迭代器是否与另一个迭代器不相等。
        9. 等于运算符(==):判断当前迭代器是否与另一个迭代器相等。

迭代器结构ListIterator还使用了两个类型别名:
        1. Node:表示节点类型ListNode<T>。
        2. Self:表示迭代器自身类型ListIterator<T, Ref, Ptr>。

3. 迭代器和构造函数

请看示例代码:

	template<class T>
	class list
	{
		typedef ListNode<T> Node;
	public:
		// 不符合迭代器的行为,无法遍历
		//typedef Node* iterator;
		//typedef ListIterator<T> iterator;
		//typedef ListConstIterator<T> const_iterator;

		typedef ListIterator<T, T&, T*> iterator;
		typedef ListIterator<T, const T&, const T*> const_iterator;

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

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

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

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

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

该部分定义了一个双向链表类list。

list类包含以下成员变量和成员函数:
        1. typedef Node:类型别名,表示节点类型ListNode<T>。
        2. typedef iterator:类型别名,表示迭代器类型ListIterator<T, T&, T*>。
        3. typedef const_iterator:类型别名,表示常量迭代器类型ListIterator<T, const T&, const T*>。

成员函数如下:
        1. begin():返回头节点的下一个节点的迭代器,用于遍历链表。
        2. begin() const:返回头节点的下一个节点的常量迭代器,用于遍历常量链表。
        3. end():返回头节点的迭代器,用于判断迭代结束。
        4. end() const:返回头节点的常量迭代器,用于判断常量迭代结束。
        5. 构造函数:初始化头节点,将头节点的_next和_prev指向自身。

注意:由于ListIterator的设计不符合迭代器的行为,无法进行迭代,所以在list类中注释掉了typedef iterator和typedef const_iterator的定义。

3. push_back、pop_back、push_front和pop_front

请看示例代码:

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;*/

	insert(end(), x);
}

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

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

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

这部分代码实现了list类的push_back、pop_back、push_front和pop_front成员函数。

        1. push_back:在链表末尾添加一个节点。首先创建一个新的节点newnode,然后获取链表末尾的节点tail,将tail的_next指向newnode,newnode的_prev指向tail,newnode的_next指向头节点_head,头节点的_prev指向newnode。最后调用insert函数,在末尾迭代器的位置插入新节点。
        2. pop_back:删除链表末尾的节点。首先获取链表末尾节点的前一个节点,然后调用erase函数,将末尾迭代器的前一个位置的节点删除。
        3. push_front:在链表开头添加一个节点。调用insert函数,在开头迭代器的位置插入新节点。
        4. pop_front:删除链表开头的节点。调用erase函数,将开头迭代器的位置的节点删除。

4. insert和erase

请看示例代码:

		// 没有iterator失效
		iterator insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;
			Node* newnode = new Node(x);
			Node* prev = cur->_prev;

			// prev  newnode  cur
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;

			return iterator(newnode);
		}

		// erase 后 pos失效了,pos指向节点被释放了
		iterator erase(iterator pos)
		{
			assert(pos != end());

			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;

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

			delete cur;

			return iterator(next);
		}

这部分代码实现了list类的insert和erase成员函数。

        1. insert:在指定位置前插入一个节点。首先获取迭代器pos所指向的节点cur,创建一个新的节点newnode,然后获取pos的前一个节点prev。将prev的_next指向newnode,newnode的_prev指向prev,newnode的_next指向cur,cur的_prev指向newnode。最后返回一个指向新节点的迭代器。
        2. erase:删除指定位置的节点。首先断言pos不等于end(),即pos不是指向末尾的迭代器。然后获取迭代器pos所指向的节点cur,分别获取cur的前一个节点prev和后一个节点next。将prev的_next指向next,next的_prev指向prev。删除cur节点,并释放内存。最后返回一个指向删除节点后的节点的迭代器。

        需要注意的是,在使用erase函数删除节点时,要确保操作前的迭代器pos不会失效,否则会导致未定义行为。

        到此我们只是简单的模拟实现了一下STL中list的相关接口~,后续我们会一一展开学习的,希望这篇博客能给您带来一些启发和思考!那我们下次再一起探险喽,欢迎在评论区进行讨论~~~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于aardio web.view2库和python playwright包的内嵌浏览器自动化操作

通过cdp协议可以实现playwright操控webview。 新建Python窗口工程 修改pip.aardio 修改pip.aardio&#xff0c;并执行&#xff0c;安装playwright。 //安装模块 import process.python.pip; //process.python.path "python.exe";/* 安装模块。 参数可以用一个字…

工地/矿区/电力/工厂/环卫视频智能安全监控反光衣AI检测算法的原理及场景应用

一、引言 随着科技的快速发展&#xff0c;特别是在智能交通和安全生产领域&#xff0c;对于夜间或弱光环境下的人员识别和安全监控需求日益凸显。反光衣作为一种重要的安全装备&#xff0c;被广泛应用于道路施工、工地作业、夜间巡逻、安全生产等场景&#xff0c;旨在提高人员的…

Vue 性能革命:揭秘前端优化的终极技巧;Vue优化技巧,解决Vue项目卡顿问题

目录 Vue优化路径 一、使用key 二、使用冻结对象 三、使用函数式组件 四、使用计算属性 五、使用非实时绑定的表单项 六、保持对象引用稳定 6.1、保持对象引用稳定定义 6.2、保持对象引用稳定与不稳定的例子 6.3、vue2判断数据是否变化是通过hasChanged函数实现的 ①…

Spring AOP、Spring MVC工作原理、发展演变、常用注解

Spring AOP 概念 AOP全称为Aspect Oriented Programming&#xff0c;表示面向切面编程。切面指的是将那些与业务无关&#xff0c;但业务模块都需要使用的功能封装起来的技术。 AOP基本术语 **连接点&#xff08;Joinpoint&#xff09;&#xff1a;**连接点就是被拦截到的程序执…

智能文档革新:合合信息智能文档处理平台上线基金合同抽取模型!

一、什么是基金合同&#xff1f; 基金合同是指具有平等地位主体的基金当事人在基金活动中&#xff0c;为规范其间的权利、义务&#xff0c;依意思表示一致而形成的契约或协议。《证券投资基金法》第五十二条规定&#xff0c;公开募集基金的基金合同应当包括下列内容: &#x…

软件游戏d3dcompiler_43.dll丢失怎么办,总结几种有效的方法

在使用电脑时&#xff0c;可能会碰到找不到d3dcompiler_43.dll的问题。即在使用过程中&#xff0c;突然弹出一个提示“d3dcompiler_43.dll丢失”&#xff0c;由于此文件的缺失&#xff0c;部分程序将无法启动。为恢复正常使用&#xff0c;我们需要修复此文件。接下来&#xff0…

【C++】 解决 C++ 语言报错:Undefined Reference

文章目录 引言 未定义引用&#xff08;Undefined Reference&#xff09;是 C 编程中常见的错误之一&#xff0c;通常在链接阶段出现。当编译器无法找到函数或变量的定义时&#xff0c;就会引发未定义引用错误。这种错误会阻止生成可执行文件&#xff0c;影响程序的正常构建。本…

Java项目:基于SSM框架实现的中小企业人力资源管理系统【ssm+B/S架构+源码+数据库+开题报告+毕业论文】

一、项目简介 本项目是一套基于SSM框架实现的中小企业人力资源管理系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简…

matlab 绘制高等数学中的二维函数示例

matlab 绘制高等数学中的二维函数示例 绘制高等数学中的二维函数示例绘制结果 绘制高等数学中的二维函数示例 clc,clear,close all; % 定义方程 eqn (x, y) (x.^2 y.^2).^3 - y.^4;% 绘制方程曲线和坐标轴 ezplot(eqn, [-2, 2, -2, 2]) hold on % 在同一图形中保持绘图% 绘…

国际上备考所有云计算/IT证书的五大优质免费课程网站

最近越来越多的小伙伴来问小李哥&#xff0c;小李哥亚马逊云科技AWS认证大满贯是在哪里上课复习的呢&#xff1f;全部上付费课程那不是一笔巨款吗&#xff1f;小李哥这次来盘点备考国际上IT证书的5大优质免费课程网站(不只是亚马逊云科技AWS的课程&#xff0c;其他课程同样可以…

满足GMSL静电防护要求的方案

什么是GMSL&#xff1f;它是做什么用的&#xff1f;它有什么优点&#xff1f;设计GMSL防静电有啥难度&#xff1f; 带着这些疑问我们先了解下什么是GMSL。 一&#xff0e;简述 GMSL GMSL&#xff08;Gigabit Multimedia Serial Link&#xff09;即千兆多媒体串行链路&#xf…

odoo 物联网 设备数据采集方案

图一 架构手稿(许老师专属) 图二 架构简图 部署 方案一&#xff1a; odoo业务数据库与设备采集数据库使用一个instance。 缺点&#xff1a;重启pg服务相互影响。 方案二&#xff1a; odoo业务数据库与设备采集数据库独立部署&#xff0c;使用两个instance。 优点&#xff1a;…

一个使用率超高的大数据实验室是如何练成的?

厦门大学嘉庚学院“大数据应用实训中心”&#xff08;以下简称“实训中心”&#xff09;自2022年建成以来&#xff0c;已经成为支撑“大数据专业”复合型人才培养的重要支撑&#xff0c;目前实训中心在专业课程实验教学、项目实训、数据分析类双创比赛、毕业设计等方面都有深入…

CVPR2024自动驾驶轨迹预测方向的论文整理

2024年自动驾驶轨迹预测方向的论文汇总 1、Producing and Leveraging Online Map Uncertainty in Trajectory Prediction 论文地址&#xff1a;https://arxiv.org/pdf/2403.16439 提出针对在线地图不确定性带给轨迹预测的影响对应的解决方案。 在轨迹预测中&#xff0c;利用在…

vscode连接SSH——连接学校服务器,使用conda配置个人环境并使用

服务器的连接 在vscode远程资源管理中配置配置文件&#xff0c;如下图&#xff1a; 然后点击左下角进行连接&#xff1a; 点击需要连接的服务器&#xff0c;输入对应密码即可登录成功。 服务器上创建自己的环境 确保服务器上已安装anaconda。 先查看服务器上的conda信息&…

Linux_共享内存通信

目录 1、共享内存原理 2、申请共享内存 2.1 ftok 2.2 测试shmget、ftok 2.3 查看系统下的共享内存 3、关联共享内存 3.1 测试shmat 4、释放共享内存 4.1 测试shmctl 5、实现共享内存通信 6、共享内存的特性 结语 前言&#xff1a; 在Linux下&#xff0c;有一…

jenkins在使用pipeline时,为何没有方块形视图

项目场景&#xff1a; 安装完Jenkins时后&#xff0c;通过pipeline创建的项目任务。 问题描述 在立即构建后&#xff0c;没有显示每个阶段的视图。 原因分析&#xff1a; 原因是&#xff0c;刚安装的Jenkins&#xff0c;这个视图不是Jenkins自带的功能&#xff0c;而必须安装…

Cannot resolve symbol ‘log`

idea里的代码log变红色&#xff0c;是因为缺少Lombok插件。 安装lombok插件即可。安装完应用&#xff0c;重启软件就好了。 依次点击菜单栏中的 File → Settings&#xff08;Windows/Linux&#xff09; 或 IntelliJ IDEA → Preferences&#xff08;macOS&#xff09;。在设置…

设计模式探索:单例模式

1. 什么是单例模式? 定义: 单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一种全局访问点以访问该实例。常见的场景包括身份证号码、政府等需要唯一实例的情况。 单例模式通常用于那些需要在应用程序中仅存在一个实例的情况,例如配置管理器、线程池、数据…

ret2syscall简单总结

主要是自己的简单的学习总结。 知识点 关于系统调用如何传递参数问题&#xff0c;即系统调用约定&#xff08;syscall&#xff0c;int 80h&#xff0c;svc&#xff09;_int 80h intel汇编用法-CSDN博客 ret2syscall的做题思路&#xff08;以32位程序为例&#xff09; - ZikH…