【C++ 】list 类

news2025/1/15 20:32:34

1. 标准库中的list类

list 类 的介绍:

1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代

2. list与forward_list非常相似:最主要的不同在于forward_list是单链表

3. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好,只是不支持任意位置的随机访问

a. list 的构造函数

  • list() (无参构造函数)
  • list (const list& x) (拷贝构造)
  • list (InputIterator first, InputIterator last)

( 用[first, last)区间中的元素构造list )

  • list (size_type n, const value_type& val = value_type())

( 构造的list中包含n个值为val的元素 )

注意:

list 的迭代器是双向迭代器(完成 ++ , --),可以支持传单向迭代器( 完成 ++ ) 和双向迭代器

b. list 增删查改

  • push_back (尾插)
  • push_front (头插)
  • pop_back (尾删)
  • pop_front (头删)
  • insert (在某一位置前增加新节点)

代码举例1

#include <iostream>
#include <list>
using namespace std;

int main()
{
	list<int> lt;
	lt.push_back(0);
	lt.push_back(10);
	lt.push_back(20);
	lt.push_back(30);
	list<int> ::iterator it = lt.begin();
	++it;
	lt.insert(it,70);
	it = lt.begin();
	while(it != lt.end())
	{
		cout << *it << endl;
		++it;
	}
}

运行结果:

  • earse (删除某一位置的节点)

代码举例2

#include <iostream>
#include <list>
using namespace std;

int main()
{
	list<int> lt;
	lt.push_back(0);
	lt.push_back(10);
	lt.push_back(20);
	lt.push_back(30);
	list<int> ::iterator it = lt.begin();
	++it;
	lt.erase(it);
	it = lt.begin();
	while(it != lt.end())
	{
		cout << *it << endl;
		++it;
	}
}

运行结果:

  • swap ( 交换两个list中的元素 )

代码举例3

#include <iostream>
#include <list>
using namespace std;

int main()
{
	list<int> lt;
	lt.push_back(10);
	lt.push_back(20);
	lt.push_back(30);
	list<int> llt;
	llt.push_back(4);
	llt.push_back(5);
	llt.push_back(6);
	lt.swap(llt);
	auto it = lt.begin();
	while(it != lt.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	for (auto i : llt)
	{
		cout << i << " ";
	}
}

运行结果:

  • clear (清除有效节点,即不包括哨兵位)

c. list 容量

d. list 获取元素

e. list 迭代器

  • begin + end ( 返回第一个元素的迭代器+ 返回最后一个元素下一个位置的迭代器 )

画图分析

  • rbegin + rend ( 返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的 reverse_iterator,即begin位置 )

注意:

反向迭代器的模拟实现和我们理解的有偏差,图上为了理解,我们可以认为rbegin是最后一个元素,rend是第一个元素的前一个位置

但是实际上,rbegin指向的位置就是end的位置,rend指向的位置就是rbegin的位置,但是在解引用时,会运算符重载 *,得到该位置的上一个位置 (详情看 list 模拟实现)

2. 迭代器失效 

list 迭代器类似一个指针,指向节点的地址 (具体详情看 list 的模拟实现)

所以在发生 erase 的时候容易造成迭代器失效(即野指针)

3. list 类的模拟实现

代码

namespace lhy
{
    template<class T>
struct ListNode
{
public:
	ListNode* prev;
	ListNode* next;
	T val;
	ListNode(const T& t = T())
	{
		prev = next = nullptr;
		val = t;
	}
};
template<class T,class Ref,class Ptr>
class list_iterator
{
public:
	typedef list_iterator<T,Ref,Ptr>  self;
	list_iterator(ListNode<T>* n)
		:_node(n)
	{}
	Ptr operator->()
	{
		return &_node->val;
	}
	Ref 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 list_iterator& t)
	{
		return _node != t._node;
	}
	bool operator==(const list_iterator& t)
	{
		return _node == t._node;
	}
	ListNode<T>* _node;
};

template<class iterator,class Ref,class Ptr>
	class list_converse_iterator
	{
	private:
		iterator com;
	public:
		typedef list_converse_iterator  self;
			list_converse_iterator(iterator& it)
			:com(it)
		{}
		Ptr operator->()
		{
			return &(*com);
		}
		Ref operator*()
		{
			iterator tmp = com;
			--tmp;
			return *tmp;
		}
		self& operator++()
		{
			--com;
			return *this;
		}
		self operator++(int)
		{
			self tmp = *this;
			--*this;
			return tmp;
		}
		self& operator--()
		{
			++com;
			return *this;
		}
		self operator--(int)
		{
			self tmp = *this;
			++*this;
			return tmp;
		}
		bool operator!=(const self& t)
		{
			return com != t.com;
		}
		bool operator==(const self& t)
		{
			return com == t.com;
		}
	};
	template<class T>
	class List
	{
	public:
		typedef list_iterator<T, T&, T*> iterator;
		typedef list_iterator<T, const T&, const T*>  const_iterator;
		typedef list_converse_iterator<iterator, T&, T*>  converse_iterator;
		typedef list_converse_iterator<iterator, const T&,const T*>  const_converse_iterator;
		List()
		{
			node = new ListNode<T>;
			node->next = node;
			node->prev = node;
		}
		iterator begin()
		{
			return iterator(node->next);
		}
		const const_iterator begin() const
		{
			return const_iterator(node->next);
		}
		iterator end()
		{
			return iterator(node);
		}
		const const_iterator end() const
		{
			return const_iterator(node);
		}
		converse_iterator rbegin()
		{
			return converse_iterator(end());
		}
		const_converse_iterator rbegin() const
		{
			return const_converse_iterator(end());
		}
		converse_iterator rend()
		{
			return converse_iterator(begin());
		}
		const_converse_iterator rend()
		{
			return const_converse_iterator(begin());
		}
	void push_back(const T& val)
	{
		ListNode<T>* ptail = node->prev;
		ListNode<T>* newnode = new ListNode<T>(val);
		ptail->next = newnode;
		newnode->next = node;	
		newnode->prev = ptail;
		node->prev = newnode;
	}
	void push_front(const T& x)
	{
		insert(begin(), x);
	}
	void insert(iterator pos,const T &x)
	{
		ListNode<T>* cur = pos._node;
		ListNode<T>* newnode = new ListNode<T>(x);
		newnode->next = cur;
		newnode->prev = cur->prev;
		cur->prev->next = newnode;
		cur->prev = newnode;
	}
	void earse(iterator pos)
	{
		ListNode<T>* cur = pos._node;
		assert(cur != node);
		ListNode<T>* _prev = cur->prev;
		ListNode<T>* _next = cur->next;
		_prev->next = _next;
		_next->prev = _prev;
		delete cur;
	}
private:
	ListNode<T>* node;
};
}

list 迭代器的实现 

单看这一个类的实现,可能会疑惑,已经有一个 List 类了,为什么还要加一个 list_iterator 类,并且很容易发现,两个类的成员变量是一样的

如:list<int> :: iterator it;

我们希望 *it 得到的是T类型的变量(这里是int 类型)

而 it++ 得到的是下一个节点的地址

如果是只有 List 类,无法实现

因为如果 typedef ListNode* iterator

那么 *it 的类型就是 ListNode;

it++ 也不是下一个结点的地址(这是链表,开辟的空间不是连续的)

所以这里的 list_iterator 类是为了运算符重载 *和++

代码注意事项

可以和下面的对应


 

注意:

const iterator 修饰的是 (ListNode<T>*),即指针不可以更改,但是指针所指向的内容可以更改

const_iterator 修饰的是 const ListNode<T>* ,即指针所指向的内容不可更改

注意:

对于这个运算符重载,实际写的时候只要写一个 -> 就行,编译器简化两个 ->

模板第一个传的是 正向迭代器,利用正向迭代器来实现反向迭代器的功能

这里 运算符重载* 让正向迭代器--再解引用,是为了得到 原先T 类型的数据的前一个数据,原因如下:

这里传递是 end() ,但是 对应的数据不是我们想要的

才会在解引用得到前一个数据的值

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

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

相关文章

汇编语言(Assemble Language)学习笔记(更新中)

零.学习介绍和使用工具 【1】我们使用的教材是机械工业出版社的《32位汇编语言程序设计第二版》。 指导老师是福州大学的倪一涛老师。 这门课程教授的是Intel 80*86系列处理器的32位汇编。我们现在的处理器都兼容这个处理器。 这篇博客只是大二下汇编语言学习的总结&#xff…

城乡居民基本医疗信息管理系统|基于Springboot的城乡居民基本医疗信息管理系统设计与实现(源码+数据库+文档)

城乡居民基本医疗信息管理系统目录 目录 基于Springboot的城乡居民基本医疗信息管理系统设计与实现 一、前言 二、系统设计 三、系统功能设计 1、病例管理 2、医院资讯信息管理 3、医院资讯类型管理 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选…

第十四次CCF-CSP(第二题 买菜、第四题 再卖菜)

第十四次CCF-CSP 第二题 买菜 原题链接&#xff1a;3263. 买菜 - AcWing题库 思路分析 简单来说&#xff0c;就是给出两组区间的集合A,B 求出两集合中相交区间的部分的长度&#xff0c;注意若区间 [s,t] 是相交的&#xff0c;则长度为 t-s 。 思路一 因为数据量比较小&am…

传输层的UDP协议

1. UDP协议报文格式 1.1 16位端口号 UDP协议报文中&#xff0c;端口号占2个字节&#xff0c;包括 源端口号 和 目的端口号。 1.2 16位UDP长度 UDP报文长度为2个字节 &#xff0c;即UDP数据报长度为0~65535&#xff0c;也就是64kb。 1.3 16位UDP检验和 数据在网络传输的…

Opencv4.5读取视频文件失败的原因

0. 写在前面 这篇短文是对上期编译的一个补充&#xff1a;Windows11OpenCV4.5Qt5.9.1安装教程_opencv4.5.4 windows11安装-CSDN博客 1. 问题现象 上篇博文是读取图片数据成功&#xff0c;结果今天做项目&#xff0c;测试视频文件和录像时&#xff0c;发现capture.isOpened()返…

ROS2组件component自定义实现

ROS2系列文章目录 ROS2中nav_msgs/msg/Path 数据含义及使用 ROS2中std_msgs/msg/Header 数据含义及使用 ROS中TF变换详解 ROS2中launch编写及参数含义&#xff08;launch.xml、python&#xff09; 提示&#xff1a;阅读并实践本文档后&#xff0c;将掌握并理解ros1中nodele…

Python面向对象析构函数你学会了吗?

​ 当我第一次接触Python面向对象编程时&#xff0c;我完全被析构函数的概念给搞懵了。但是&#xff0c;随着我深入研究&#xff0c;我发现它其实并没有那么复杂。 1.析构函数是什么 析构函数是Python面向对象编程中的一个重要概念&#xff0c;它是在对象生命周期结束时自动调…

网工内推 | 浪潮原厂售前、方案经理,上市公司大平台,最高20K

01 浪潮集团 招聘岗位&#xff1a;售前经理 职责描述&#xff1a; 1、负责数字生态、数字教育、工业互联网、数字乡村、智慧城市、智慧交通等数字经济领域解决方案的售前工作&#xff0c;包括但不限于融合方案编制、项目调研、订制文档输出及客户咨询支持。 2、负责与公司各产…

数据完整性

Oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 数据完整性 数据完整性是关系数据库的一个重要特征&#xff0c;一般包含实体完整性、参照完整性和用户自定义完整性 3 种 实体完整性 实体完整性&#xff1a;规定表中的每…

VXLAN学习笔记

声明&#xff1a;该博客内容大部分参考参考链接整理 什么是VXLAN&#xff1f; VXLAN(Virtual Extensible LAN)即虚拟扩展局域网&#xff0c;是大二层网络中广泛使用的网络虚拟化技术。在源网络设备与目的网络设备之间建立一条逻辑VXLAN隧道&#xff0c;采用MAC in UDP的封装方…

Python的asyncio 多线程

-- 多线程、进程、协程是什么就不讲了&#xff0c;&#xff08;就是你理解的一边呼吸&#xff0c;一边看文章&#xff09; 仅解决问题的话&#xff0c;下边两篇不用看&#xff0c; Python 中的 async await 概念-CSDN博客 再深一点的看这个 Python中的多线程、进程、协程、…

Vue.js+SpringBoot开发个人健康管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 健康档案模块2.2 体检档案模块2.3 健康咨询模块 三、系统展示四、核心代码4.1 查询健康档案4.2 新增健康档案4.3 查询体检档案4.4 新增体检档案4.5 新增健康咨询 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpri…

【项目设计】基于Httplib和Mysql的视频播放

项目源码&#xff08;绝对可以直接运行&#xff09; 一、项目介绍 1. 对视频播放系统的认识 搭建视频共享播放服务器&#xff0c;可以让所有人通过浏览器访问服务器&#xff0c;实现视频的上传查看&#xff0c;以及管理并播放的功能。主要是完成服务器端的程序业务功能的实现…

腾讯云轻量4核8G12M服务器性能如何?价格感人

腾讯云轻量4核8G12M服务器配置446元一年&#xff0c;646元12个月&#xff0c;腾讯云轻量应用服务器具有100%CPU性能&#xff0c;系统盘为180GB SSD盘&#xff0c;12M带宽下载速度1536KB/秒&#xff0c;月流量2000GB&#xff0c;折合每天66.6GB流量&#xff0c;超出月流量包的流…

springboot276基于JS的个人云盘管理系统的设计与实现

个人云盘管理系统设计与实现 摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装个人云盘管理系统软件来发挥其…

开发反应式API

开发反应式API 开发反应式API1 使用SpringWebFlux1.1 Spring WebFlux 简介1.2 编写反应式控制器 2 定义函数式请求处理器3 测试反应式控制器3.1 测试 GET 请求3.2 测试 POST 请求3.3 使用实时服务器进行测试 4 反应式消费RESTAPI4.1 获取资源4.2 发送资源4.3 删除资源4.4 处理错…

大话设计模式——7.抽象工厂模式(Abstract Factory Pattern)

1.介绍 抽象工厂模式是工厂模式的进一步优化&#xff0c;提供一个创建一系列相关或相互依赖对象的接口&#xff0c;而无需指定它们具体的类。属于创建型模式。 UML图&#xff1a; 2.示例 车辆制造工厂&#xff0c;不仅可以制造轿车也可以用来生产自行车。 1&#xff09;Abs…

【技术类-02】python实现docx段落文字的“手动换行符(软回车)”变成“段落标记(硬回车)”

作品展示 背景需求&#xff1a; 制作周计划时&#xff0c;需要将周计划docx内所有的表格里的手动换行符&#xff08;软回车&#xff09;”变成“段落标记&#xff08;硬回车&#xff09;”&#xff0c; 全部改成段落标记&#xff08;硬回车&#xff09; 但是19份docx每份都要打…

人工智能入门学习笔记1:什么是人工智能

一、什么是人工智能 人工智能(Artificial Intelligence)&#xff0c;是一个以计算机科学&#xff08;Computer Science&#xff09;为基础&#xff0c;由计算机、心理学、哲学等多学科交叉融合的交叉学科、新兴学科&#xff0c;研究、开发用于模拟、延伸和扩展人的智能的理论、…

day06、07-MySQL

文章目录 一、MySQL概述1.1 安装1.2 数据模型1.3 SQL简介1.3.1 SQL通用语法1.3.2 分类 二. 数据库设计-DDL2.1 项目开发流程2.2 数据库操作2.2.1 查询数据库2.2.2 创建数据库2.2.3 使用数据库2.2.4 删除数据库 2.3 图形化工具2.3.1 介绍2.3.2 安装2.3.3 使用2.2.3.1 连接数据库…