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

news2025/1/15 6:23:54

一、list的结构

1. list的节点

list的底层是一个带头双向循环链表,但list本身和list的节点是不同的结构,需要分开实现。

list节点的结构:

在这里插入图片描述

template<class T>
struct list_node
{
	list_node<T>* _next;
	list_node<T>* _prev;
	T _data;
	//构造:使用x初始化节点的数据
	list_node(const T& x)
		:_next(nullptr)
		, _prev(nullptr)
		, _data(x)
	{}
};

2. list的结构

class list
{
	typedef list_node<T> node;
	//……
private:
	node* _head;
	size_t _size;
};

二、list的迭代器

list的迭代器不能再像vector一样用普通指针作为迭代器,因为存储节点的空间不保证连续。

list迭代器必须有能力指向list的节点,并能够正确的进行++、--、*、->等操作,所谓正确是指:++时指向下一个节点,--时指向上一个节点,*取的是节点的数据值,->取的是节点数据成员的地址。

在这里插入图片描述

list的迭代器使用封装 + 操作符重载,以达到在使用方式上如同指针一样的目的。

list是一个双向链表,迭代器必须具有前移++、后移- -,所以list的迭代器是一个双向迭代器。

1. 正向迭代器

template<class T, class Ref, class Ptr>
struct __list_iterator
{
	typedef list_node<T> node;
	typedef __list_iterator<T, Ref, Ptr> Self;
	node* _pnode;//迭代器内部要有一个指针,指向list的节点
	
	//constructor
	__list_iterator(node* p)
		:_pnode(p)
	{}
	
	Ptr operator->()
	{
		//return &_pnode->_data;
		return &(operator*());//标准做法
	}

	// iterator it
	// *it
	// ++it;
	// 解引用取到的是节点的数据值
	Ref operator*()
	{
		return _pnode->_data;
	}

	const T& operator*() const
	{
		return _pnode->_data;
	}
	
	// ++it
	Self& operator++()
	{
		_pnode = _pnode->_next;
		return *this;
	}

	// it++
	Self operator++(int)//返回类型为迭代器自己
	{
		//拷贝构造,这里只需要浅拷贝,使用系统生成的即可满足需求
		Self tmp(*this);
		_pnode = _pnode->_next;
		return tmp;
	}
	//--it
	Self& operator--()
	{
		_pnode = _pnode->_prev;
		return *this;
	}
	//it--
	Self operator--(int)
	{
		Self tmp(*this);
		_pnode = _pnode->_prev;
		return tmp;
	}
	//比较指向节点的指针即节点的地址
	bool operator!=(const Self& it) const
	{
		return _pnode != it._pnode;
	}

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

2. 反向迭代器

反向迭代器的实现体现了泛型思维。反向迭代器是正向迭代器的封装,内部有一个正向迭代器成员

模板参数Iterator:给我不同容器的正向迭代器,适配出对应的这个容器需要的反向迭代器。比如要实现vector的反向迭代器只需将其正向迭代器作为参数传入即可。

// 适配器 -- 复用
template<class Iterator, class Ref, class Ptr>
class ReverseIterator
{
	typedef ReverseIterator<Iterator, Ref, Ptr> Self;
private:
	Iterator _it;
public:
	ReverseIterator(Iterator it)
		:_it(it)
	{}
	Ref operator* ()
	{
		Iterator tmp = _it;
		return *(--tmp);//先--再解引用原因是用正向迭代器的end构造了rbegin
	}
	Ptr operator-> ()
	{
		return &(operator*());//显示调解引用再取地址
	}
	//前置++
	Self& operator++ ()
	{
		--_it;//反向迭代器的++是正向迭代器的--
		return *this;
	}
	//后置++
	Self operator++ (int)
	{
		ReverseIterator tmp(_it);
		--_it;
		return tmp;
	}
	//前置--
	Self& operator-- ()
	{
		++_it;
		return *this;
	}
	//后置--
	Self operator-- (int)
	{
		ReverseIterator tmp(_it);
		++_it;
		return tmp;
	}
	bool operator!= (const Self& s)
	{
		return _it != s._it;
	}
};

3. 迭代器失效问题

list的一个性质:插入操作不会造成原迭代器失效。删除操作也只有”指向“被删除元素的迭代器失效,其他不受影响。

4. 具体实现

template<class T>
class list
{
	typedef list_node<T> node;
private:
	node* _head;
	size_t _size;
	//……
public:
	//前面使用模板参数是为了根据传入参数不同实现不同迭代器
	typedef __list_iterator<T, T&, T*> iterator;
	typedef __list_iterator<T, const T&, const T*> const_iterator;

	typedef ReverseIterator<iterator, T&, T*> reverse_iterator;
	typedef ReverseIterator<const_iterator, const T&, const T*> const_reverse_iterator;

	//反向迭代器
	reverse_iterator rbegin()
	{
		return reverse_iterator(end());
	}

	reverse_iterator rend()
	{
		return reverse_iterator(begin());
	}

	const_reverse_iterator rbegin() const
	{
		return const_reverse_iterator(end());
	}

	const_reverse_iterator rend() const
	{
		return const_reverse_iterator(begin());
	}

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

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

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

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

在这里插入图片描述

可以发现正向迭代器和反向迭代器是对称的,这也是为什么在反向迭代器实现中会有如下代码:

Ref operator* ()
{
	Iterator tmp = _it;
	return *(--tmp);//先--再解引用原因是用正向迭代器的end构造了rbegin
}

三、构造和析构

  • 构造一个空链表
void empty_initialize()
{
	_head = new node(T());
	_head->_next = _head;
	_head->_prev = _head;
	_size = 0;
}
list() { empty_initialize(); }//生成一个空链表
  • 区间构造
template <class InputIterator>
list(InputIterator first, InputIterator last)
{
	empty_initialize();
	while (first != last)
	{
		push_back(*first);
		++first;
	}
}
  • 拷贝构造和赋值运算符重载
//传统写法
// lt2(lt1)
//list(const list<T>& lt)
//{
//	empty_initialize();

//	for (const auto& e : lt)
//	{
//		push_back(e);
//	}
//}

 lt1 = lt3
//list<T>& operator=(const list<T>& lt)
//{
//	if (this != &lt)
//	{
//		clear();
//		for (const auto& e : lt)
//		{
//			push_back(e);
//		}
//	}

//	return *this;
//}
void swap(list<T>& lt)
{
	std::swap(_head, lt._head);
	std::swap(_size, lt._size);
}
// lt2(lt1)
list(const list<T>& lt)
//list(const list& lt) //虽然C++语法支持类名作为类型,但是不建议
{
	empty_initialize();

	list<T> tmp(lt.begin(), lt.end());
	swap(tmp);
}

// lt3 = lt1
list<T>& operator=(list<T> lt)
//list& operator=(list lt) // 不建议
{
	swap(lt);
	return *this;
}
  • 析构
void clear()
{
	iterator it = begin();
	while (it != end())
	{
		it = erase(it);
	}
}
~list()
{
	clear();//clear完成除头节点外其他节点的销毁工作
	delete _head;
	_head = nullptr;
}

四、insert和erase

//在pos前插入
iterator insert(iterator pos, const T& x)
{
	node* newnode = new node(x);
	node* cur = pos._pnode;
	node* prev = cur->_prev;

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

	++_size;

	return iterator(newnode);
}

iterator erase(iterator pos)
{
	assert(pos != end());

	node* prev = pos._pnode->_prev;
	node* next = pos._pnode->_next;

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

	delete pos._pnode;
	--_size;

	return iterator(next);
}

五、其他元素操作

void push_back(const T& x)
{
	//node* newnode = new node(x);
	//node* tail = _head->_prev;
	 _head         tail   newnode
	//tail->_next = newnode;
	//newnode->_prev = tail;
	//newnode->_next = _head;
	//_head->_prev = newnode;

	insert(end(), x);
}

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

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

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

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

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

相关文章

ELK日志(4)

搭建filebeatredislogstasheskibana架构&#xff0c;拓扑图参考ELK&#xff08;3&#xff09;安装redis&#xff0c;并启动&#xff08;1&#xff09;准备安装和数据目录[rootes ~]# mkdir -p /opt/redis_cluster/redis_6379/{conf,logs,pid}&#xff08;2&#xff09;下载redi…

CES 2023:推动低碳化与数字化,英飞凌塑造可持续未来

在参展CES 2023的3200多家企业当中&#xff0c;英飞凌虽然在规模上还比不上某些国际科技巨头&#xff0c;但是其展示的内容却相当引人瞩目。作为一家创立于1999年的高科技企业&#xff0c;英飞凌在全球总计拥有56个研发机构&#xff0c;20个生产工厂&#xff0c;其技术实力之雄…

fiddler 抓手机的包

目录 一、fiddler抓手机包的介绍 二、一个前提&#xff0c;及配置 1.前提 2.前提配置 三、两大步 1.设置fiddler 2.设置手机 一、fiddler抓手机包的介绍 Fiddler是一款非常流行并且实用的http抓包工具&#xff0c;它的原理是在本机开启了一个http的代理服务器&#xff…

分享111个JavaScript源码,总有一款适合您

JavaScript源码 分享111个JavaScript源码&#xff0c;总有一款适合您 源码下载链接&#xff1a;https://pan.baidu.com/s/1aUIpouX5nTwW1FF-8lStnw?pwdjh3v 提取码&#xff1a;jh3v 采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 ​ 下面是文件的名字&#xff0c;我放…

Golang Web Application

Golang Web Application GoLang Web App基本设置 1.导入库 导入fmt和net/http,http建立一个/和编写一个indexPage的函数,fmt编写一个写Hello world! package mainimport ("fmt""net/http" )func main() {http.HandleFunc("/", indexPage)http.L…

[数据结构复习]自用大纲

内容多基于王道和李春葆《数据结构教程》&#xff0c;做复习提纲之用 基本内容回顾 顺序队 队列是线性表&#xff08;具有逻辑上的前驱后继关系&#xff09;。头插尾删&#xff0c;先进先出。 队列的实现至少需要维护如下内容&#xff08;一数组&#xff0c;二指针&#xff…

电脑开机屏幕闪烁后变成蓝屏无法启动怎么办?

电脑开机屏幕闪烁后变成蓝屏无法启动怎么办&#xff1f;有用户在将电脑开机之后&#xff0c;出现了屏幕会闪动的情况&#xff0c;接着电脑屏幕就变成蓝屏的了。而且再次启动的时候也是这样。这个情况下是我们的系统出现了问题&#xff0c;我们来看看如何去使用U盘进行系统重装的…

python+django大学生体质测试管理系统

系统分为学生和管理员&#xff0c;教师三个角色 学生的主要功能有&#xff1a; 1.学生注册和登陆系统 2.查看系统的公告资讯信息 3.学生查看体质测试的项目&#xff0c;下载测试文件 4.学生留言板在线留言 5.学生个人中心修改个人资料&#xff0c;修改密码 6.学生个人中心查询我…

Java 介绍与环境搭建

文章目录Java 介绍与环境搭建Java 背景介绍Java 背景故事Java 三大平台Java SEJava MEJava EEJava 跨平台工作原理平台与跨平台跨平台工作原理JDK 下载和安装下载 JDK安装 JDK第一个 Java 程序HelloWorld 介绍Java 程序开发的三个步骤HelloWorld 案例的编写和运行HelloWord 案例…

如何通过Terraform Associate考试并获得证书

1 什么是Terraform? Terraform是一个IaC工具&#xff0c;IaC全称为Infrastructure as Code&#xff0c;基础设施即代码。它的理念是通过代码来管理基础设施&#xff0c;如服务器、数据库等&#xff0c;更多请看《Terraform入门教程&#xff0c;示例展示管理Docker和Kubernete…

mysql快速生成100W条测试数据(5)商品销售数据并存入mysql数据库

这是之前的文章里面包含一些以前的一些操作流程可以进行参考学习 更加详细操作步骤在第一篇文章里面 mysql快速生成100W条测试数据&#xff08;1&#xff09;&#xff1a;游戏人物数据 mysql快速生成100W条测试数据&#xff08;2&#xff09;公司员工信息 mysql快速生成100W条测…

Linux USB实现网络共享

usb 网络共享 两个Linux设备之间实现USB网络共享&#xff0c;类似Android手机开启USB网络共享的功能。其中一台设备为USB Host&#xff0c;另外一台为USB Device。Device使用的USB接口必须为USB Slave&#xff0c;否则无法正常工作。使用RNDIS驱动&#xff0c;还能够通过USB与…

【ROS2入门】介绍 eloquent 版本中 turtlesim 和 rqt 使用

大家好&#xff0c;我是虎哥&#xff0c;从今天开始&#xff0c;我将花一段时间&#xff0c;开始将自己从ROS1切换到ROS2&#xff0c;在上一篇中&#xff0c;我们Jeston TX1 核心模块&#xff0c;JetPack_4.6.3刷机后环境中安装了 ROS2 eloquent版本&#xff0c;并完成了初步的…

C语言-动态内存分配(12.1)

目录 思维导图&#xff1a; 1.为什么存在动态内存分配 2.动态内存函数的介绍 2.1 malloc 2.2 free 2.3 calloc 2.4 realloc 3.常见的动态内存错误 写在最后&#xff1a; 思维导图&#xff1a; 1.为什么存在动态内存分配 我们现在学习了一些内存开辟的方式&#xff1a…

IDEA如何把自己改动的代码一次性发给别人

背景介绍&#xff1a; 想把自己改动的代码同步给同事 方式1&#xff1a;通过git&#xff0c;把自己本地代码 push 到代码托管平台&#xff0c;同事从代码托管平台 pull 最新代码&#xff1b; 方式2&#xff1a;通过IDEA的Patch包的方式来快速发送修改的代码&#xff08;步骤如下…

pr值高的域名对网站有什么价值?怎么在线查询搜狗PR权重

众所周知一个全新的搜狗域名在建立网站后搜索引擎会对其进行一个类似资格评价的阶段&#xff0c;我们将这个阶段称为沙盒&#xff0c;在沙盒里面的这段时间&#xff0c;我们将其称为沙盒期&#xff0c;沙盒期一般都是1-3个月。在沙盒期这段时间内。我们仍需要经常更新文章。所以…

联合证券|海南自贸港快速发展,概念股出炉!

海南自由贸易港货物进出口 初次打破2000亿元关口 1月13日&#xff0c;国新办就2022年全年进出口状况举行发布会。会上介绍道&#xff0c;近两年海南自由贸易港货物进出口快速开展&#xff0c;继2021年头次打破1000亿元后&#xff0c;2022年再上新台阶&#xff0c;初次打破2000…

Pytorch深度学习【十二】

填充和步幅 卷积核带来的问题—输入形状不断减小更大的卷积核可以更快的减小输出大小 形状从nh∗nwn_h * n_wnh​∗nw​减少到 (nh−kh1)∗(nw−kw1)(n_h-k_h1)*(n_w-k_w1)(nh​−kh​1)∗(nw​−kw​1) 解决方案 填充—在输入周围添加额外的行/列—一般用0填充理论依据 填充p…

Odoo 16 企业版手册 - 库存管理之报告

报告 正确监控库存操作将帮助您轻松分析和理解库存管理的工作流程。Odoo可以为您提供一个专用平台&#xff0c;特别是用于为您的组织中完成的库存操作生成报告&#xff0c;并在图形和表格视图中准确表示数据。您可以实时自定义报告&#xff0c;以根据您的要求获得细致的报告。在…

Nvm,Nrm使用教程

NVM介绍 NVM全称node.js version management &#xff0c;专门针对node版本进行管理的工具&#xff0c;通过它可以安装和切换不同版本的node.js 使用场景 我目前的公司有很多项目&#xff0c;其中有一些老项目用的是vue2.5左右了webpack版本也比较低&#xff0c;只能使用10.16.…