STL之my_list容器

news2024/9/21 14:40:16

前言:各位老铁好久不见了,今天分享的知识是自己实现一个简单的list容器,为什么我先跳过vector容器的自我实现呢?我个人觉得vector相对于list的自我实现简单一点,所以今天先分享实现my_list的知识

我们要实现my_list,首先我们要知道list有那些常用的接口,我们先看一看实现list的文档

实现list容器文档入口

在这里插入图片描述
list容器有这么多接口,我会从里面挑出常用的接口进行模拟实现。

list实现(这里讲的是带头的双向循环链表)

我们在数据结果中已经学习过list了,知道list内部结果主要包含三部分,一是存放的值,二是链接前面一个结点的指针,三是链接后面一个结点的指针。由于库里面也有已经实现好了的list,为了防止my_list和库里面的冲突,我们需要搞个命名空间封装起来,命名空间的名字随便用。

namespace ljy
{

}

由于我们不知道list容器的类型是什么类型,所以我们需要搞个模板函数,对任意类型的list都适用。然后我们需要搞个类(私有的比较好,防止其他人修改成员变量)把list里面的成员给封装起来

namespace ljy
{
	template<class T>
	struct _list_node
	{
		_list_node<T>* _prev;
		_list_node<T>* _next;
		T _data;//任意类型的数据
	};
}

接下来我们需要把链表给初始化

namespace ljy
{
	template<class T>
	struct _list_node
	{
		_list_node<T>* _prev;
		_list_node<T>* _next;
		T _data;//任意类型的数据

		_list_node(const T& x = T())//这里是构造一个空的对象来充当缺省参数,防止没有提供参数从而导致编译器报错
			:_data(x)
			, _prev(nullptr)
			, _next(nullptr)
		{}
	};
}

由于list不支持随机访问,我们只能通过迭代器进行访问list中的结点。所以我们先来实现list的迭代器的接口。

由于list迭代器底层是一个指针,所以我们如果需要访问list和修改list,就需要对指针进行解引用和取地址,因此我给list迭代器模板参数定义三个参数,一个是有关数据类型(T),一个是解引用(Ref),一个是取地址(Ptr)

//迭代器
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迭代器进行初始化

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

我们需要分清对指针的*和->,一个是对指针解引用(表示取向指针指向的值),另一个是对指针取地址(表示指向结点的本身)

	//*
	Ref operator*()
	{
		return _node->_data;
	}
//->
Ptr operator->()
{
	return &_node->_data;
}

再接下来我们实现++和- -运算符的重载++和- - 分为前置++,前置- -,后置++和后置- -。前置和后置的区别是是否先返回值再进行++和- -

前置++

//前置++
Self& operator++()//返回的是this指针,出了作用域还存在,所以用引用返回
{
	_node = _node->_next;
	return *this;
}

后置++

//后置++       返回的是中间变量,出了作用域就不存在了,不需要使用引用返回
Self operator++(int)//在形参里面+int是为了和前置++区别开来。
{
	Self tmp(this);
	++(*this);
	return tmp;
}

前置- -

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

后置- -

//后置--
Self operator--(int)
{
	Self tmp(this);
	_node = _node->_prev;
	return tmp;
}

然后再实现==和!=运算符重载

==运算符重载

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

!=运算符重载

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

到这里list的迭代器就完成了。

接下来就到list的一些常用接口的实现

list的框架

template <class T>
	class list
	{
		typedef _list_node<T> Node;
	public:
		typedef _list_iterator<T,T*,T&> iterator;//普通迭代器
		typedef _list_iterator<T, T*, T&> const_iterator;//const迭代器
	private:
		Node* _head;
	};

返回头节点

//双向带头的循环链表(可修改头节点的位置和头节点指向的值)
iterator begin()
{
	//开始的结点在头节点的下一个结点
	//通过迭代器迭代寻找结点
	return iterator(_head->_next);
}
//通过const迭代器迭代寻找结点(不可修改头节点的位置和头节点指向的值)
const_iterator begin() const
{
	return const_iterator(_head->_next);
}

//双向带头的循环链表(可修改尾节点的位置和尾节点指向的值)

	iterator end()
	{
		//头结点指向的位置就是end位置
		return iterator(_head);
	}

//通过const迭代器迭代寻找结点(不可修改尾节点的位置和尾节点指向的值)

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

然后实现在任意位置插入数据的接口


		//在pos位置前插入数据
		void insert(iterator pos,const T& x)
		{
			//先找出当前的结点
			Node* cur = pos._node;
			Node* prev = cur->_prev;

			//再开辟出一个新的结点
			Node* newnode = new Node(x);

			//再把prev newnode cur三个结点按前后顺序链接起来
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
		}

再在任意插入位置插入数据的接口基础上实现头插和尾插

头插:就是在begin前一个位置插入

//头插
void push_front(const T& x)
{
	//直接在begin()前面的位置进行插入
	insert(begin(), x);
}

尾插:就是在头结点前一个位置进行插入,头节点前一个位置就是末尾
在这里插入图片描述

	//从尾部插入数据
	void push_back(const T& x)
	{
		//end()就是头节点的前一个位置,也就是尾部
		return (end(), x);
	}

到这里插入数据的接口我们就实现完了,有插入数据必然就会有删除数据,所以接下来我们就开始实现删除数据。

首先我们先实现在任意位置删除数据,在文档中erase删除数据是将数据删除后返回当前数据的下一个位置。
在这里插入图片描述
在这里插入图片描述

	//在任意位置删除数据
	void erase(iterator pos, const T& x)
	{
		//不能把头节点删除
		assert(pos != end());
		//先保存当前结点
		Node* cur = pos._node;
		Node* prev = cur->_prev;
		Node* next = cur->_next;
		delete cur;

		//再把结点链接起来
		prev->_next = next;
		next->_prev = prev;
	}

然后在在任意位置删除数据的接口的基础上,再实现头删和尾删。

头删:begin()函数返回的位置就是头

void pop_front(const T& x)
{
	erase(begin(), x);
}

尾删:end()函数获取的位置是尾结点的下一个位置,只要先给end()函数- -就能找到尾结点从而删掉尾结点。

//尾删
void pop_back(const T& x)
{
	erase(--end());
}

list的删除数据接口到这里就完成了,然后我们再给list写初始化函数(个人习惯,先写完接口再写初始化),构造函数,拷贝构造函数,赋值运算符重载,析构函数。

构造函数:由于我们我们这里的list是双向带头循环的链表,所以我们需要先开辟一个头节点出来,把头结点和自身链接起来。
在这里插入图片描述

list()
{
	//new出一个头结点
	_head = new Node;
	//让头节点指向自己
	_head->_next = _head;
	_head->_prev = _head;
}

拷贝构造函数:

//拷贝构造
//lt2(lt1)
list(const list<T>& lt)//传一个类对象过来
{
	//先构造出一个和lt一模一样的头节点,然后再直接把lt的数据插入到lt2中
	_head = new Node;
	_head->_next = _head;
	_head->_prev = _head;

	for (auto e : lt)
	{
		push_back(e);//这里其实是this->push_back()
	}
}

赋值运算符重载(现代写法:借助第三方实现相对应的功能,再把原来的和第三方进行交换)

//operator=
//lt3=lt2
list<T>& operator=(list<T> lt)//这里创建对象借助了拷贝构造,lt2通过lt1进行拷贝构造
{
	//直接把lt3和lt2的头结点进行交换就可以了
	swap(_head = lt._head);

	//再返回this指针
	return *this;
}

总结:这次的文章分享list容器的底层代码知识,让我们懂得如何实现list容器的一些常用接口,希望我们能通过理解list容器的底层代码,从而加深对list容器的使用的理解。

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

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

相关文章

machine learning - 2

泛化误差 也可以认为是预测时的误差。 训练误差 并不是越小越好&#xff0c;太小会过拟合。 获得测试集合的方法&#xff1a; 1&#xff09;&#xff1a; 2&#xff09;&#xff1a;例如&#xff1a;k-折交叉验证法&#xff0c; 就的每k个数据取一个座位测试集 3&#xff0…

nginx容器映射配置文件后,启动一直报错提示:failed (13: Permission denied)的排查

问题现象&#xff1a; 使用harbor 的install.sh 创建docker-compose之后&#xff0c;出现nginx容器一直重启。 查看日志发现是&#xff1a;配置文件无权限。报错信息如下&#xff1a; Sep 2 16:43:13 172.28.0.1 nginx[1344]: 2024/09/02 08:43:13 [emerg] 1#0: open() “/e…

网站网站建设公司用什么

随着互联网的飞速发展&#xff0c;网站已经成为企业的重要门面和宣传工具。为了在网上展示自己的品牌形象和吸引更多的客户&#xff0c;越来越多的企业选择找专业的网站建设公司进行网站建设。那么&#xff0c;网站建设公司主要使用什么技术和方法呢&#xff1f; 首先&#xff…

四、基本电路设计笔记——4.1 DC-DC稳压电路

目录 4.1 DC-DC稳压电路 4.1.1 基于MT2492的DC-DC稳压电路 &#xff08;1&#xff09;芯片参数 &#xff08;2&#xff09;芯片引脚 &#xff08;3&#xff09;输出电压设置 4.1.2 基于MT2499A的DC-DC稳压电路 &#xff08;1&#xff09;芯片参数 &#xff08;2&#xf…

【Redis】Redis 主从复制

文章目录 1 前言2 主从模式介绍3 配置 Redis 主从结构3.1 建立复制3.2 断开复制3.3 其他特性3.4 拓扑结构 4 Redis 主从复制原理4.1 复制过程4.2 PSYNC 数据同步4.3 PSYNC 运行流程 5 主从复制流程5.1 全量复制流程5.2 部分复制流程5.3 实时复制流程 1 前言 分布式系统中存在一…

鸿誉移民:定制化移民服务,吹响全球高效率移民的嘹亮号角!

鸿誉移民&#xff1a;定制化移民服务&#xff0c;吹响全球高效率移民的嘹亮号角&#xff01; 作为国内知名海外移民服务机构&#xff0c;鸿誉移民历经多年行业沉淀&#xff0c;拥有着极其丰富的移民咨询以及移民办理经验&#xff0c;并以咨询及时精准&#xff0c;签证快捷、通…

Bean 的实例化(创建 | 获取)

Spring为Bean提供了多种实例化方式&#xff0c;包括如下4种方式&#xff1a; 第一种&#xff1a;通过构造方法实例化第二种&#xff1a;通过简单工厂模式实例化第三种&#xff1a;通过factory-bean实例化&#xff08;工厂方法模式实例化&#xff09;第四种&#xff1a;通过Fact…

智能未来:低代码与AI如何重塑企业应用开发

引言 在当今瞬息万变的商业环境中&#xff0c;企业面临着前所未有的挑战与机遇。数字化转型已经成为各行各业的必然趋势&#xff0c;而在这一过程中&#xff0c;应用开发的效率与智能化程度成为企业竞争力的重要衡量标准。传统的开发模式往往需要大量的时间和资源&#xff0c;而…

【Godot4.3】基于ShapePoints的Polygon2D扩展

概述 这同样是来自2023年7月份的一项实验性工作&#xff0c;基于最初版本的ShapePoints静态函数库&#xff0c;实现了对Polygon2D节点的扩展&#xff0c;用于创建参数化图形的Polygon2D节点。 Polygon2D节点本身只能通过顶点绘制工具&#xff0c;创建很随意的多边形。通过Sha…

MySQL数据库管理系统下载安装

一. MySQL概述&#xff1a; 1.数据库相关概念 数据库&#xff1a;存储数据的仓库&#xff0c;数据是有组织地进行存储&#xff08;DataBase 简DB&#xff09;数据库管理系统&#xff1a;操纵和管理数据库的大型软件 &#xff08;DataBase Management System 简DBMS)SQL:操…

一分钟创建自己的分班查询系统,家长扫码即可进群

开学后&#xff0c;老师们的忙碌也达到了顶峰。整理教材、准备课程计划、布置教室&#xff0c;这些工作已经让人应接不暇&#xff0c;更别提还要处理分班事宜。以往&#xff0c;老师们需要一个个通知家长分班结果&#xff0c;这不仅耗时耗力&#xff0c;还容易出错。家长们也常…

​数字IC设计基本概念之多时钟设计​

当设计中使用了多个时钟时&#xff0c;这些时钟域之间的关系可能是synchronous、asynchronous或者exclusive的。如下所示&#xff1a; Synchronous&#xff1a; Asynchronous&#xff1a; Exclusive&#xff1a; 需要人为地指定设计中时钟之间的关系&#xff0c;EDA工具才能正…

燃油车淘汰倒计时开始了?

文 | AUTO芯球 作者 | 璇子 新能源车要取代燃油车了&#xff1f; 油车车主先别喷啊 就在上个月 新能源乘用车月销量数据一经公布 我一看 渗透率居然达到了惊人的51% 啥概念啊 如果卖100台车 51台都是新能源 其他49台才是燃油车 看到这数据 有好多看热闹的人就在说 …

Vue组件:创建组件、注册组件、使用组件

1、创建组件 组件&#xff08;component&#xff09;是 Vue.js 最强大的功能之一。通过开发组件可以封装可服用的代码&#xff0c;将封装好的代码注册成标签&#xff0c;扩展 HTML 元素的功能。几乎任意类型应用的界面都可以抽象为一个组件树&#xff0c;而组件树可以用独立可…

【完-移动云-基础】移动云架构和ECS

一、移动云产品架构 产品架构分为IaaS、PaaS、SaaS 我司使用的是IaaS&#xff0c;仅托管了服务器资源。软件则由浪潮提供。 一图理解三者区别 二、云主机ECS 是一种弹性按需提供的云端服务器&#xff0c;可选择不同的配置 基础服务&#xff1a; 虚拟化、镜像、云硬盘、云…

autoware整体架构的分析

autoware framework sensinglidar driver&#xff08;lidar驱动&#xff09;PointCloud Preprocessing&#xff08;点云预处理&#xff09;Detection&#xff08;检测&#xff09;GNSS (全球导航卫星系统)IMU (惯性测量单元) Localization&#xff08;定位&#xff09;Pose Ini…

产品需求过程管理重要性

产品需求过程管理重要性 背景 以下都是真实事项经历回顾&#xff0c;在产品开发过程中&#xff0c;产品经理与研发团队之间的沟通至关重要。然而&#xff0c;沟通不畅或信息缺失常常导致需求无法准确传达&#xff0c;最终影响产品的成功。以下是一些常见的问题&#xff1a; 1.需…

Qt插件开发总结6--插件间依赖

文章目录 一、前言二、基本策略三、效果展示四、关键代码4.1、主程序4.2、插件管理器4.3、插件A 一、前言 插件大致可分为&#xff1a;功能性插件、界面插件&#xff1b;一个软件由一堆插件堆起来&#xff0c;必然难以避免插件间相互引用&#xff0c;例如&#xff1a;插件A调用…

安防监控/视频汇聚EasyCVR视频监控平台级联上级,无法播放是什么原因?

EasyCVR视频监控平台&#xff0c;作为一款智能视频监控综合管理平台&#xff0c;凭借其强大的视频融合汇聚能力和灵活的视频能力&#xff0c;在各行各业的应用中发挥着越来越重要的作用。EasyCVR视频汇聚平台采用先进的图像处理技术和传输协议&#xff0c;能够确保高清、稳定的…

Qt (13)【Qt窗口 —— 颜色对话框 QColorDialog】

阅读导航 引言一、颜色对话框 QColorDialog简介二、常用方法介绍⭕参数说明 三、使用示例 引言 在深入探讨了Qt的QMessageBox之后&#xff0c;我们现在转向QColorDialog&#xff0c;一个让用户轻松选择颜色的实用对话框&#xff0c;为Qt应用增添色彩选择的便捷性。 一、颜色对…