C++ ——STL容器【list】模拟实现

news2024/11/16 5:35:50

在这里插入图片描述

代码仓库:

list模拟实现

list源码

数据结构——双向链表

文章目录

  • 🍇1. 节点结构体
  • 🍈2. list成员
  • 🍉3. 迭代器模板
  • 🍊4. 迭代器
  • 🍋5. 插入删除操作
    • 🍌5.1 insert & erase
    • 🍌5.2 push_back & push_front & pop_back & pop_front
  • 🍍6. 构造 & 析构 & 拷贝构造
  • 🥭7. 赋值重载
  • 🍓8. 获取元素个数

🍇1. 节点结构体

源码的list是双向带头循环链表,所以我们定义两个节点,一个指向下一个,一个指向前一个

template<class T>
struct list_node
{
	list_node<T>* _next;	//指向下一个节点
	list_node<T>* _prev;	//指向前一个
	T _val;	//数据
	list_node(const T& val = T())
		:_next(nullptr)
		, _prev(nullptr)
		, _val(val)
	{}
};

🍈2. list成员

list类包含一个_head头节点,然后为了方便查出当前有多少个节点,还能多定义一个_size

template<class T>
class list
{
    typedef list_node<T> Node;
public:
    // 各类操作方法
    //...
private:
    Node* _head;
    size_t _size;
}

🍉3. 迭代器模板

image-20230730105307091

源码的迭代器设置了三个模板参数:

  • T:表示指向list节点的数据类型
  • Ref:迭代器的引用类型,通常情况为T&,但也可表示const
  • Ptr:表示指向节点的指针类型,通常情况下为T*,但也可表示const迭代器,避免代码的冗余

对于string或者是vector的迭代器,对其解引用就可以表示当前的数据;而list是链表,解引用之后表示的一个节点,所以相对会麻烦一点

//Ref T& / const T&
//Ptr T* / const T*
template<class T, class Ref, class Ptr>
struct __list_iterator
{
	typedef __list_iterator<T, Ref, Ptr> self;
	typedef list_node<T> Node;
	Node* _node;
	__list_iterator(Node* node)
		:_node(node)
	{}


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

	Ptr operator->()
	{
		return &_node->_val;
	}
	//前置++
	self& operator++()
	{
		_node = _node->_next;
		return *this;
	}
	//后置++
	self operator++(int)
	{
		self tmp(*this);
		_node = _node->_next;
		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;
	}

};

Tips:

迭代器并没有写拷贝构造,那么就是默认浅拷贝。这无关影响,因为我们就是希望通过这个迭代器找到这个节点

void test1()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
    //浅拷贝
	list<int>::iterator it = lt.begin();
    while(it!=lt.end())
    {
        cout<< *it << " ";
        ++it;
    }
    cout<<endl;
}

这里没有奔溃也是因为迭代器没有写析构函数,迭代器只是负责访问,并不负责管理

🍊4. 迭代器

const const_iterator begin() const
{
	//单参数构造函数 隐式类型转换
	return _head->_next;
}
const const_iterator end() const
{
	return _head;
}


iterator begin()
{
	//单参数构造函数 隐式类型转换
	return _head->_next;
}
iterator end()
{
	return _head;
}

🍋5. 插入删除操作

🍌5.1 insert & erase

这里插入删除操作之后,也会存在当前迭代器失效,所以传修改完毕之后的迭代器位置

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

	prev->_next = tmp;
	tmp->_next = cur;

	cur->_prev = tmp;
	tmp->_prev = prev;
	++_size;
	return tmp;
}
iterator erase(iterator pos)
{
	assert(pos != end());
	Node* cur = pos._node;
	Node* next = cur->_next;
	Node* prev = cur->_prev;

	prev->_next = next;
	next->_prev = prev;
	delete cur;
	--_size;
	return next;
}

🍌5.2 push_back & push_front & pop_back & pop_front

写了指定位置插入删除之后,直接复用即可

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

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

void pop_back()
{
	Node* tail = _head->_prev;
	erase(tail);
}
void pop_front()
{
	erase(begin());
}

🍍6. 构造 & 析构 & 拷贝构造

查看源码发现list的构造和析构都采用了复用

image-20230730115520781
清空链表

void clear()
{
	iterator it = begin();
	while (it != end())
	{
		it = erase(it);
	}
	//_size = 0;
}

复用

void empty_init()
{
	_head = new Node;
	_head->_next = _head;
	_head->_prev = _head;
	_size = 0;
}

list()
{
	empty_init();
}

list(const list<T>& lt)
{
	empty_init();
	for (auto& e : lt)
	{
		push_back(e);
	}
}
~list()
{
	clear();
	delete _head;
	_head = nullptr;
}

🥭7. 赋值重载

这里还是采用现代的写法,交换完毕之后,自动调用析构函数

void swap(list<T>& lt)
{
	std::swap(_head, lt._head);
	std::swap(_size, lt._size);
}
list<T>& operator=(list<T> lt)
{
	swap(lt);
	return *this;
}

🍓8. 获取元素个数

size_t size()
{
	return _size;
}

以上就是list的基本功能实现,实质上就是双向带头循环链表,迭代器这块有点复杂。

那本期分享就到这里咯,我们下期再见,如果还有下期的话。

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

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

相关文章

Python实现GA遗传算法优化循环神经网络回归模型(LSTM回归算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 遗传算法&#xff08;Genetic Algorithm&#xff0c;GA&#xff09;最早是由美国的 John holland于20世…

Zabbix分布式监控Web监控

目录 1 概述2 配置 Web 场景2.1 配置步骤2.2 显示 3 Web 场景步骤3.1 创建新的 Web 场景。3.2 定义场景的步骤3.3 保存配置完成的Web 监控场景。 4 Zabbix-Get的使用 1 概述 您可以使用 Zabbix 对多个网站进行可用性方面监控&#xff1a; 要使用 Web 监控&#xff0c;您需要定…

【GitOps系列】监听镜像版本变化触发 GitOps工作流

文章目录 前言工作流总览安装和配置 ArgoCD Image Updater创建 Image Pull Secret&#xff08;可选&#xff09;创建 Helm Chart 仓库创建 ArgoCD Application删除旧应用&#xff08;可选&#xff09;配置仓库访问权限创建 ArgoCD 应用 体验 GitOps 工作流总结 前言 在【GitOps…

AQS之ReentrantLock源码详解

一、管程 管程&#xff1a;指的是管理共享变量以及对共享变量的操作过程&#xff0c;让它们支持并发 互斥&#xff1a;同一时刻只允许一个线程访问共享资源 同步&#xff1a;线程之间如何通信、协作 MESA模型 在管程的发展史上&#xff0c;先后出现过三种不同的管程模型&a…

Xamarin.Android实现加载中的效果

目录 1、说明2、代码如下2.1 图1的代码2.1.1、创建一个Activity或者Fragment&#xff0c;如下&#xff1a;2.1.2、创建Layout2.1.3、如何使用 2.2 图2的代码 4、其他补充4.1 C#与Java中的匿名类4.2 、其他知识点 5、参考资料 1、说明 在实际使用过程中&#xff0c;常常会用到点…

【142. 环形链表 II】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0…

TCP三次握手报文代码实现

声明 看了一个博主写的文章 但是因为自己电脑环境的问题最终没有运行 本文仅对思路进行一个讲解 大家就看一下这个思路就好 不好意思大家测试环境 window10系统 哈工大的泰山服务器 检验和部分 原理 首先让检验和部分为0(二进制) 然后将左边的部分依次相加 然后将地址分成如图…

如何用arduino uno主板播放自己想要的曲子。《我爱你中国》单片机版本。

目录 一.效果展示 二.基本原理 三.电路图 四.代码 一.效果展示 arduino播放《我爱你中国》 二.基本原理 利用arduino uno单片机实现对蜂鸣器振动频率的调节&#xff0c;基于PWM控制系统通过代码实现控制。 三.电路图 四.代码 //main.uno #define Buzzer 2int PotBuffer …

PHP使用Redis实战实录4:单例模式和面向过程操作redis的语法

PHP使用Redis实战实录系列 PHP使用Redis实战实录1&#xff1a;宝塔环境搭建、6379端口配置、Redis服务启动失败解决方案PHP使用Redis实战实录2&#xff1a;Redis扩展方法和PHP连接Redis的多种方案PHP使用Redis实战实录3&#xff1a;数据类型比较、大小限制和性能扩展PHP使用Re…

第六章 HL7 架构和可用工具 - 定义新的消息类型和结构类型

文章目录 第六章 HL7 架构和可用工具 - 定义新的消息类型和结构类型编辑数据结构和代码表 第六章 HL7 架构和可用工具 - 定义新的消息类型和结构类型 消息类型标识消息并与 HL7 MSH:9 字段中的值匹配。定义消息类型时&#xff0c;指定发送消息结构类型&#xff08;可能与消息类…

OpenMP

官方文档&#xff1a;OpenMP | LLNL HPC Tutorials OpenMP总览 统一内存访问&#xff1a;OpenMP、Pthreads 非统一内存访问&#xff1a;MPI OpenMP与Pthread OpenMP原理 串行区到达并行区后会派生多个线程&#xff0c;并行区代码执行完后进行线程合并&#xff0c;剩下主线程 编…

SqueezeLM 的想法,压缩输入句子潜变量,生成下一句子

又搞了一段时间。还是感觉LongNet那种空洞注意力做编码器有搞头。 RetNet等AFT方法&#xff0c;直接生成太长的句子感觉有点难度&#xff0c;不过可以一句句生成&#xff0c;每次生成短句&#xff0c;这样感觉比较合适。 启发 受 MemroyTransformer 和 GLM 启发 想了一个类似…

大会第二日,精彩不停!LiveVideoStackCon 2023 上海站

历时四个月的精心筹备&#xff0c;LiveVideoStackCon 组委会与联席主席、出品人及评审团通力合作&#xff0c;90余位讲师对演讲内容的反复琢磨……只为呈现最专业的音视频技术盛会。 今日&#xff0c;以「沉浸新视界」为主题的LiveVideoStackCon 2023 上海站 音视频技术大会继续…

ACL原理

ACL原理 ACL是一种用于控制网络设备访问权限的技术&#xff0c;可以通过配置ACL来限制特定用户、应用程序或网络设备对网络资源的访问。 1、ACL&#xff08;Access Control List&#xff09; 2、ACL是一种包过滤技术。 3、ACL基于IP包头的IP地址、四层TCP/UDP头部的端口号、…

Flowable-服务-微服务任务

目录 定义图形标记XML内容界面操作 定义 Sc 任务不是 BPMN 2.0 规范定义的官方任务&#xff0c;在 Flowable 中&#xff0c;Sc 任务是作为一种特殊的服务 任务来实现的&#xff0c;主要调用springcloud的微服务使用。 图形标记 由于 Sc 任务不是 BPMN 2.0 规范的“官方”任务…

WebAgent-基于大型语言模型的代理程序

大型语言模型&#xff08;LLM&#xff09;可以解决多种自然语言任务&#xff0c;例如算术、常识、逻辑推理、问答、文本生成、交互式决策任务。最近&#xff0c;LLM在自主网络导航方面也取得了巨大成功&#xff0c;代理程序助HTML理解和多步推理的能力&#xff0c;通过控制计算…

【Linux多线程】详解线程控制、线程分离

线程互斥与同步 &#x1f478; 理解线程&#x1f934;pthead_t&#x1f977;关于线程&#x1f9b8;‍♀️线程控制POSIX线程库线程ID及进程地址空间布局 &#x1f9b8;线程分离__thread关键字&#x1f9b8;‍♂️pthread_detach函数&#x1f9b9;‍♀️pthread_exit函数&#x…

[JavaWeb]SQL介绍-DDL-DML

SQL介绍-DDL-DML 一.SQL简介1.简介2.SQL通用语法3.SQL语言的分类 二.DDL-操作数据库与表1.DDL操作数据库2.DDL操作表①.查询表(Retrieve)②.创建表(Create)③.修改表(Update)④.删除表(Delete) 三.Navicat的安装与使用四.DML-操作表数据1.添加(Insert)2.修改(Update)3.删除(Del…

【C++】反向迭代器的模拟实现通用(可运用于vector,string,list等模拟容器)

文章目录 前言一、反向迭代器封装&#xff08;reverseiterator&#xff09;1.构造函数1解引用操作.3.->运算符重载4.前置&#xff0c;后置5.前置--&#xff0c;后置--6.不等号运算符重载7.完整代码 二、rbegin&#xff08;&#xff09;以及rend&#xff08;&#xff09;1.rb…

[nlp] TF-IDF算法介绍

&#xff08;1&#xff09;TF是词频(Term Frequency) 词频是文档中词出现的概率。 &#xff08;2&#xff09; IDF是逆向文件频率(Inverse Document Frequency) 包含词条的文档越少&#xff0c;IDF越大。