【C++ 】list 类

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.mfbz.cn/a/459524.html

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

相关文章

707.(力扣)设计链表 C++

1、题目描述 理解题目&#xff1a;这道题目设计链表的五个接口 获取链表第index个节点的数值在链表的最前面插入一个节点在链表的最后面插入一个节点在链表第index个节点前面插入一个节点删除链表的第index个节点 2、设计单链表 &#xff08;1&#xff09;这里是单链表的设计…

Python:函数的形参与实参

注意&#xff1a;本文引用自专业人工智能社区Venus AI 更多AI知识请参考原站 &#xff08;[www.aideeplearning.cn]&#xff09; 函数基本概念 在Python中&#xff0c;函数是一种将代码封装以进行重复使用的机制。它们允许你定义一段代码&#xff0c;以便在程序的多个位置调…

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

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

Linux中防火墙相关操作

一、查看防火墙状态 可通过两种方式查看防火墙状态&#xff0c;一种通过systemctl命令&#xff0c;另一种是通过firewall-cmd命令。 1、systemctl status firewalld 2、firewall-cmd --state 二、关闭防火墙 1、暂时关闭&#xff1a;设置暂时关闭防火墙将会在下次重启系统后失…

学点Java打小工_Day4_Homework

1 统计数字 1 int[] scores{0,0,1,2,3,5,4,5,2,8,7,6,9,5,4,8,3,1,0,2,4,8,7,9,5,2,1,2,3,9}; 求出上面数组中0-9分别出现的次数 &#xff08;双重for循环&#xff09; Testpublic void solveProblem1() {int[] scores {0,0,1,2,3,5,4,5,2,8,7,6,9,5,4,8,3,1,0,2,4,8,7,9,5,2,…

javaEE7

1. <% page pageEncoding"UTF-8"%><% page import"java.io.*"%> <% page import"java.util.*"%> <% page import"java.math.*"%> <html> <head><title>网站计数器</title></head&…

单例各样方式的写法

单例简介 特点 内存中只有一个实例&#xff0c;节约内存&#xff0c;无需频繁创建&#xff0c;减少性能开销&#xff0c;提高系统运行效率使用者无需关心类创建过程&#xff0c;整个项目中任何地方、任何时间开箱即用 缺点 单例模式没有抽象&#xff0c;扩展会有很大困难单例类…

LeetCode 热题 100 | 回溯(二)

目录 1 39. 组合总和 2 22. 括号生成 3 79. 单词搜索 菜鸟做题&#xff0c;语言是 C&#xff0c;感冒快好版 关于对回溯算法的理解请参照我的上一篇博客&#xff1b; 在之后的博客中&#xff0c;我将只分析回溯算法中的 for 循环。 1 39. 组合总和 题眼&#xff1a;c…

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…

c++入门你需要知道的知识点(上)

&#x1fa90;&#x1fa90;&#x1fa90;欢迎来到程序员餐厅&#x1f4ab;&#x1f4ab;&#x1f4ab; 今日主菜&#xff1a;c入门 主厨&#xff1a;邪王真眼 所属专栏&#xff1a;c专栏 主厨的主页&#xff1a;Chef‘s blog 前言&#xff1a; 咱也是好久没有更…

【实战】VMware17虚拟机以及Centos7详细安装教程

文章目录 前言技术积累VMware虚拟机的安装下载VMware安装文件VMware安装步骤VMware配置密匙 虚拟机中安装centos7准备工作创建虚拟机步骤1 自定义安装步骤2 硬盘兼容性步骤3 安装客户机操作系统步骤4 选择客户机操作系统步骤5 命名虚拟机步骤6 处理器配置步骤7 设置虚拟机内存步…

Django之Cookie

Django之Cookie 目录 Django之Cookie介绍Django操作Cookie设置Cookie浏览器查看Cookie 获取Cookie设置超时Cookie注销Cookie 模拟登录验证登录验证装饰器登录验证装饰器-升级版 介绍 当我们上网使用社交媒体或者购物时&#xff0c;浏览器需要通过一种方式来记住我们。想象一下…

构造函数、原型、instanceof运算符

通过构造函数创建对象 构造函数是学习面向对象的基础 任何函数都有原型对象 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.…

Linux--基本知识入门

一.几个基本知识 终端: CtrlAltT 或者桌面/文件夹右键,打开终端切换为管理员: sudo su 退出:exit查看内核版本号: uname -a内核版本号含义: 5 代表主版本号;13代表次版本号;0代表修订版本号;30代表修订版本的第几次微调;数字越大表示内核越新. 二.目录…

ADC架构I:Flash转换器

目录 简介 量化噪声模型 量化噪声模型 量化噪声与输入信号之间的相关性容易令人误解 SNR、处理增益和FFT噪底的关系 简介 接触ADC或DAC时您一定会碰到这个经常被引用的公式&#xff0c;用于计算转换器理论信噪比 (SNR)。与其盲目地相信表象&#xff0c;不如从根本上了解其…

单目测距+姿态识别+yolov8界面+车辆行人跟踪计数

yolov5单目测距速度测量目标跟踪&#xff08;算法介绍和代码&#xff09; 1.单目测距实现方法 在目标检测的基础上&#xff0c;我们可以通过计算物体在图像中的像素大小来估计其距离。具体方法是&#xff0c;首先确定某个物体的实际尺寸&#xff0c;然后根据该物体在图像中的像…

Linux编译器gcc/g++的功能与使用

一、程序的生成 首先&#xff0c;我们知道程序的编译分为四步&#xff1a; 1、预处理 2、编译 3、汇编 4、链接 1.1预处理 预处理功能主要包括头文件展开、宏定义、文件包含、条件编译、去注释等。 所谓的头文件展开就是在预处理时候&#xff0c;将头文件内容拷贝至源文…

【优选算法】专题1 -- 双指针 -- 移动零

前言: &#x1f4da;为了提高算法思维&#xff0c;我会时常更新这个优选算法的系列&#xff0c;这个专题是关于双指针的练习 &#x1f3af;个人主页&#xff1a;Dream_Chaser&#xff5e;-CSDN博客 一.移动零&#xff08;easy&#xff09; 描述&#xff1a; 「数组分两块」是⾮…

构建部署_Docker常用命令

构建部署_Docker常见命令 启动命令镜像命令容器命令 启动命令 启动docker&#xff1a;systemctl start docker 停止docker&#xff1a;systemctl stop docker 重启docker&#xff1a;systemctl restart docker 查看docker状态&#xff1a;systemctl status docker 开机启动&…

Netty网络编程(一)

Netty网络编程&#xff08;一&#xff09; 如何进行网络通信 Socket通信是进程通讯的一种方式&#xff0c;通过调用这个网络库的一些API函数可以实现分布在不同主机的相关进程之间的数据交换 网络编程的基本流程是什么&#xff1f; 服务端先创建socket套接字&#xff0c;然后用…
最新文章