【STL容器】list

2023-09-16 11:19:27


一、list定义

list本质是一个双向带头循环链表

在这里插入图片描述

template<class T>
struct list_node
{
	list_node* prev;
	T val;
	list_node* next;
};

template<class T>
class list
{
	typedef list_node<T> node; 
private:
	node* head;
};

二、list的迭代器

不同于vector的迭代器,list的迭代器是一个类,原因是list是链式空间,普通指针的++,–不能访问到正确的地址,因此需要运算符重载++,–等。

template<class T, class ref, class ptr>
struct list_iterator
{
private:
	typedef list_node<T> node;
	typedef list_iterator<T, ref, ptr> self;
public:
	node* _cur;
};

这里有一个重点,模板参数ref,ptr是干什么的?
在list类中是这样定义iterator
typedef list_iterator<T, T&, T*> iterator;
typedef list_iterator<T, const T&, const T*> const_iterator;
这表示 ref传的是T的引用,ptr传的是T的指针,这不是多此一举吗?
但偏偏list的源码也是这样的。
在这里插入图片描述
这是为什么呢?
我们将list_iterator封装为一个类,主要是想重载它的++,–等运算符,以便正确使用iterator;
下面这是list_literator的前置++,它的返回值应该是 list_iterator<T>&,但如果我使用的是const_iterator,它的返回值应该是const list_iterator<T>&,二者的区别仅仅只是返回值不同,但仅仅返回值不同,不能构成函数重载。这样仅仅想靠一个类list_iterator是不行的,那就只能再写一个类const_list_iterator.

返回值 operator++()
{
	_cur = _cur->next;
	return *this;
}

但如果传参时,T& | const T& 作为一个参数传递,就可以只写list_iterator,来完成iterator 和 const_iterator.虽然本质上还是创建了2个类,但创建的工作由编译器解决,我们只用手写一个类就够了。

template<class T, class ref, class ptr>
	struct list_iterator
	{
	private:
		typedef list_node<T> node;
		typedef list_iterator<T, ref, ptr> self;
		//iterator 则 ref = T&
		//const_iterator 则 ref = const T&
	public:
		//成员变量
		node* _cur = nullptr;
		//迭代器:构造,++, *
		list_iterator(node* it = nullptr)
		{
			_cur = it;
		}
		self& operator++()    
		{
			_cur = _cur->_next;
			return *this;
		}
		self& operator++(int)
		{
			self tmp(*this);
			_cur = _cur->_next;
			return tmp;
		}
		ref operator*() const
		{
			return _cur->_val;
		}
	};

ptr同理,它主要服务operator->()

ptr operator->() const
{
	return &(_cur->_val);
}

注:这里有个小知识,operator的返回值可能让人感到疑惑。

在这里插入图片描述
C++ 中的 operator-> 被用于重载类的成员访问操作符 ->。这个操作符通常用于访问类的成员函数和成员变量,而且在许多情况下,它应该返回一个指向类的成员的指针。这是因为 -> 操作符通常用于访问类的成员,而类的成员可以是函数或变量。


三、list的元素操作

list的插入删除,本质就是双向带头链表的插入删除,下面给出一些简单的模拟实现。

list(const T& t = T())
{
	head = new node;
	node* tmp = new node;
	tmp->_val = t;
	tmp->_next = head;
	tmp->_prev = head;
	head->_next = tmp;
	head->_prev = tmp;
	++_size;
}
list(const list<T>& t)
{
	for (auto& e : t)
	{
		push_back(e);
	}
}
~list()
{
	iterator it = begin();
	while (it != end())
	{
		it = erase(it);
	}
	delete head;
	head = nullptr;
}
void swap(list<T>& lt)
{
	std::swap(head, lt.head);
	std::swap(size, lt.head);
}
list<T>& operator=(list<T> lt)
{
	swap(lt);
}
//iterator
iterator begin()
{
	return iterator(head->_next);
}
iterator end()
{
	return iterator(head);
}
//capacity
size_t size()
{
	return _size;
}
//modify
void push_back(const T& t = T())
{
	insert(end(), t);
}
void push_front(const T& t = T())
{
	insert(begin(), t);
}
void insert(iterator pos, const T& t)
{
	node* cur = pos._cur;
	node* prev = cur->_prev;
	node* tmp = new node;
	tmp->_val = t;
	tmp->_prev = prev;
	tmp->_next = cur;
	prev->_next = tmp;
	cur->_prev = tmp;
	++_size;
}
void pop_back()
{
	iterator it(head->_prev);
	erase(it);
}
void pop_front()
{
	erase(begin());
}
iterator erase(iterator pos)
{
	node* cur = pos._cur;
	node* prev = cur->_prev;
	node* next = cur->_next;
	prev->_next = next;
	next->_prev = prev;
	delete cur;
	--_size;
	return iterator(next);
}

list在使用时,一定要注意迭代器失效的问题。

四,list的优缺点

STL(标准模板库)中的 std::list 是一个双向链表(doubly linked list)的实现,它具有一些优点和缺点,适用于不同的使用情况。

优点:

  1. 插入和删除效率高: std::list 对于插入和删除操作非常高效,因为在双向链表中,插入和删除元素只需要改变相邻节点的指针,而不需要移动其他元素。

  2. 稳定的迭代器: 在插入和删除元素时,std::list 保持了迭代器的有效性。这意味着在遍历列表时进行插入或删除操作不会导致迭代器失效,这在其他容器中(如 std::vector)是不成立的。

  3. 内存管理灵活: 由于链表的节点可以分散存储在内存中,std::list 具有一定的内存分配灵活性。这可以避免动态数组(如 std::vector)可能会发生的重新分配和复制开销。

缺点:

  1. 随机访问效率低: std::list 不支持常数时间的随机访问,因为要遍历链表需要 O(n) 的时间复杂度。如果需要频繁的随机访问元素,std::vector 更适合。

  2. 性能较差的缓存局部性: 由于链表节点在内存中是离散分布的,因此对于大型数据集,std::list 的性能可能受到缓存局部性的影响,而数组式容器更有可能利用好缓存。

  3. 不支持随机访问和下标访问: std::list 不支持像数组那样的随机访问或下标访问,这限制了其在某些应用中的使用。

更多推荐

007-第一代软件需求整理

第一代软件需求整理文章目录第一代软件需求整理项目介绍需求来源需求来源1:竞品软件分析需求来源2:医生(市场)需求来源3:项目组内部需求来源4:软件组内部需求来源5:软件开发成员需求来源6:法律和法规总结一下关键字:Qt、Qml、需求、类型、采集项目介绍欢迎来到我们的QML&C++项目!这个项目结合了QML(QtMeta

Unity shader内置standard代码解析

最近有相关需求制作,所以这里编写一个文档,方便后续的流程查看。下载源码由于unity内置的shader是无法查看源码的,你需要去官网下载对应版本内置源码查看在引擎下载那里,会有一个BuiltinShaders,下载打开以后,就是对应的shader内置的shandard在DefaultResourcesExtra目录内,

更快,更稳,更智能,科聪穿梭车(RGV)快速构建方案!

随着自动化物流发展,密集存储得到越来越广泛地应用,已经是现代物流的重要组成部分之一。作为密集存储系统中关键设备之一,穿梭车(RGV)越来越受到大家的重视。穿梭车(RGV)是一种智能机器人,可以编程实现取货、运送、放置等任务,并可与上位机、调度系统或WMS系统进行通讯,结合RFID、条码等识别技术,实现自动化识别、存取等

Git:Git的一些基本操作

文章目录基本认识使用方法创建本地仓库配置本地仓库工作区、暂存区、版本库的概念添加文件版本回退撤销修改删除操作基本认识首先要对Git有一个基本的认知:Git本质上是一个版本控制器,可以对一个信息的多个版本进行一些控制,而能对版本的控制的好处就是,不管需要哪个版本的内容,都可以借助Git这个工具找到所需要的信息Git是一个

API接口在电商商品数据获取中的应用

一、引言在当前的数字化时代,电子商务平台已经成为了人们购物的主要场所之一。许多电商平台都提供了API接口,以便开发者可以获取商品数据并进行创新应用。本文将深入探讨如何使用API接口获取商品数据,以及如何将这些数据应用到电商领域中。二、API接口概述1.API接口定义API(ApplicationProgrammingI

Java-List<Map>的复制 深拷贝与浅拷贝

博客背景是Java开发。讲一讲List<Map>的复制中深拷贝与浅拷贝。文章目录1、浅拷贝1.1循环遍历复制1.2使用list实现类的构造方法1.3addAll方法2、深拷贝深拷贝工具类SerializationUtils.clone1、浅拷贝Map除了基本类型是值传递,其余的都是引用地址传递。由于map的value存

MySql(随记)

一条MySql执行过程首先Mysql的架构分为两层,Server层和存储引擎层。Server层:MySql大多数核心功能,主要包括,连接器,查询缓存,解释器,预处理器,优化器,执行器等存储引擎层:负责数据的获取和存储。(innodb,MyISAM)连接器1.首先经过TCP三次握手,随后进行权限验证,若有问题则返回“Ac

UI美工设计岗位的基本职责概述(合集)

UI美工设计岗位的基本职责概述11、有良好的美术功底、设计新颖,整体配色及设计创意理念,能够独立完成整个网站页面设计及制作;2、熟练运用DIV+CSS,HTML设计制作网页;3、熟练运用Photoshop,Dreamweaver,Coreldraw(或Illustrator),Flash,Fireworks等软件。4、

两个有序链表序列的交集

已知两个非降序链表序列S1与S2,设计函数构造出S1与S2的交集新链表S3。输入格式:输入分两行,分别在每行给出由若干个正整数构成的非降序序列,用−1表示序列的结尾(−1不属于这个序列)。数字用空格间隔。输出格式:在一行中输出两个输入序列的交集序列,数字间用空格分开,结尾不能有多余空格;若新链表为空,输出NULL。输入

红黑树插入的实现

红黑树:1.概念:红黑树的性质:红黑树的插入操作:其前面的插入和二叉搜索树的一模一样,只是后面需要判断是否满足红黑树的性质:具体分为三种情况:1.uncle节点存在且为红色的:对应如图:这种情况只需要将parent和uncle节点都弄成黑色,对应的grandparent节点弄成黑色接下来又会分为三种情况:1.如果对应的

创新未来,工信部组建【AI应用工作组】助力人工智能进步

随着人工智能大模型技术的快速发展和成熟,AI应用已经从早期的概念阶段进入了千行百业的实践落地阶段,三百六十行、行行需AI。如今,AI已经成为推动各行各业创新和发展的重要引擎,对经济、社会和文化的发展产生了深远的影响。为了进一步推动人工智能应用的落地和创新,工业和信息化部工业文化发展中心于9月12日上午在北京王府井希尔顿

热文推荐