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

在这里插入图片描述

代码仓库:

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的基本功能实现,实质上就是双向带头循环链表,迭代器这块有点复杂。

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

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

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

相关文章

flask处理表单数据

flask处理表单数据 处理表单数据在任何 web 应用开发中都是一个常见的需求。在 Flask 中&#xff0c;你可以使用 request 对象来获取通过 HTTP 请求发送的数据。对于 POST 请求&#xff0c;可以通过 request.form 访问表单数据。例如&#xff1a; from flask import Flask, r…

电子鼻毕业论文

面向压埋探测的人体代谢气体识别方法的研究与应用 实现对非目标气体的检测 数据预处理 &#xff08;1a&#xff09;标准化 将采集到的数据先进行变换&#xff0c;统一数量级。其中&#xff0c;xij为第j个传感器的第i个采样值&#xff1b;xj为第 j 个气体传感器的所有采样值&…

Java课题笔记~Maven基础

2、Maven 基础 2.1 Maven安装与配置 下载安装 配置&#xff1a;修改安装目录/conf/settings.xml 本地仓库&#xff1a;存放的是下载的jar包 中央仓库&#xff1a;要从哪个网站去下载jar包 - 阿里云的仓库 2.2 创建Maven项目

计算机网络——学习笔记

付费版&#xff1a;直接在上面的CSDN资源下载 免费版&#xff1a;https://wwsk.lanzouk.com/ijkcj13tqmyb 有疑问或者错误的地方可以在评论区指出&#xff0c;我会尽快回复 示例图&#xff1a;

SOC FPGA之HPS模型设计(一)

目录 一、建立HPS硬件系统模型 1.1 GHRD 1.2 从0开始搭建HPS 1.2.1 FPGA Interfaces 1.2.1.1 General 1.2.1.2 AXI Bridge 1.2.1.3 FPGA-to-HPS SDRAM Interface 1.2.1.4 DMA Peripheral Request 1.2.1.5 Interrupts 1.2.1.6 EMAC ptp interface 1.2.2 Peripheral P…

[JAVAee]线程池

目录 线程池的作用 线程池的使用 线程池的创建方式 线程池的解析 ①Executors与ThreadPoolExecutor ②ThreadPoolExecutor线程池的构造方法 ③RejectedExecutionHandler线程池的拒绝策略 固定线程数量线程池的简单模拟实现 线程池的作用 对于线程的使用,可能会频繁的创建…

首批!棱镜七彩通过汽车云-汽车软件研发效能成熟度模型能力评估

2023年7月25-26日&#xff0c;由中国信息通信研究院、中国通信标准化协会联合主办的“2023年可信云大会”隆重召开。会上&#xff0c;在中国信息通信研究院云计算与大数据研究所副所长栗蔚的主持下&#xff0c;中国信通院发布了“2023年上半年可信云评估结果”&#xff0c;并由…

uniapp checkbox radio 样式修改

文章目录 通过查看代码&#xff0c;发现 before部分是设置样式的主要属性 我们要设置的话&#xff0c;就要设置checkbox::before的属性。 其中的content表示内容&#xff0c;比如内部的对勾 那么我们设置的时候&#xff0c;比如设置disabletrue的时候或者checkedtrue的时候&…

onnxruntime (C++/CUDA) 编译安装

一、克隆及编译 git clone --recursive https://github.com/Microsoft/onnxruntime cd onnxruntime/ git checkout v1.8.0如果克隆的时候报错&#xff1a; 执行以下&#xff1a; apt-get install gnutls-bin git config --global http.sslVerify false git config --global h…

Git初始化

查看git版本 git --version 设置Git的配置变量 方法&#xff1a; 修改全局文件&#xff08;用户主目录下.gitconfig&#xff09;修改系统文件&#xff08;如/etc/gitconfig&#xff09; 用户姓名和邮件地址 修改用户名和邮件地址 git config --global user.name "用…

《JeecgBoot系列》JeecgBoot(ant-design-vue)实现筛选框:支持下拉搜索+下拉多选+表字典(支持条件查询)功能

JeecgBoot(ant-design-vue)实现筛选框&#xff1a;支持下拉搜索下拉多选表字典(支持条件查询)功能 JSearchMultiSelectTag.vue源文件 一、需求介绍 在使用JeectBoot(ant-design-vue)设计表单时&#xff0c;需要实现下拉搜索下拉多选表字典(支持条件查询)。 但是系统目前有两…

PysparkNote006---pycharm加载spark环境

pycharm配置pyspark环境&#xff0c;本地执行pyspark代码 spark安装、添加环境变量不提了 File-Settings-Project-Project Structure-add content root添加如下两个路径 D:\code\spark\python\lib\py4j-0.10.7-src.zipD:\code\spark\python\lib\pyspark.zip 2023-07-26 阴 于…

linux(进程)[6]

管理概念 先描述&#xff0c;再组织 进程 启动一个软件就相当于启动了一个进程 Linux下执行一条命令就在系统层面创建了一个进程&#xff01;&#xff01; 如何管理 进程对应的代码和数据 进程对应的PCB结构体 PCB&#xff08;process control block&#xff09; 在Linu…

Banana Pi BPI-KVM – 基于 Rockchip RK3568 SoC 的 KVM over IP 解决方案

Banana Pi 已经开始开发基于 Rockchip RK3568 SoC 的 BPI-KVM 盒&#xff0c;但它不是迷你 PC&#xff0c;而是 KVM over IP 解决方案&#xff0c;旨在远程控制另一台计算机或设备&#xff0c;就像您在现场一样&#xff0c;例如能够打开和关闭连接的设备、访问 BIOS 等。 商业…

数据结构之顺序表

一、概念及结构 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构&#xff0c;一般情况下采用数组存 储。在数组上完成数据的增删查改。 顺序表一般可以分为&#xff1a; 1. 静态顺序表&#xff1a;使用定长数组存储元素。 2. 动态顺序表&#xff1a;使用动…

【SEO基础】百度权重是什么意思及网站关键词应该怎么选?

百度权重是什么意思及网站关键词应该怎么选&#xff1f; 正文共&#xff1a;3253字 20图 预计阅读时间&#xff1a;9分钟 ​ 1.什么是网站权重&#xff1f; 这段时间和一些朋友聊到网站权重以及关键词&#xff0c;发现蛮多人对于这两个概念的认知还是存在一些错误的&#xf…

数学分析:流形的线性代数回顾

因为是线性的&#xff0c;所以可以把所有的系数都提取出去。这也是多重线性代数的性质。可以看成基本的各项自变量的乘法。 这里可以看到两个不同基向量下&#xff0c;他们的坐标转化关系。 引出了张量积&#xff0c;也就是前面提到的内容。 对偶空间的例子总是比较美好。 因为…

【EI/SCOPUS会议征稿】第三届物联网与机器学习国际学术会议(IoTML 2023)

第三届物联网与机器学习国际学术会议&#xff08;IoTML 2023&#xff09; 2023 3rd International Conference on Internet of Things and Machine Learning 2023年物联网与机器学习国际学术会议&#xff08;IoTML 2023&#xff09;将于2023年9月15-17日在新加坡召开。会议…

matlab使用教程(5)—矩阵定义和基本运算

本博客介绍如何在 MATLAB 中创建矩阵和执行基本矩阵计算。 MATLAB 环境使用矩阵来表示包含以二维网格排列的实数或复数的变量。更广泛而言&#xff0c;数组为向量、矩阵或更高维度的数值网格。MATLAB 中的所有数组都是矩形&#xff0c;在这种意义上沿任何维度的分量向量的长度…