C/C++ 入门(10)list类(STL)

news2025/1/16 15:56:14

个人主页:仍有未知等待探索-CSDN博客

专题分栏:C++

                                                        欢迎来指教!

一、标准库中的list

1、了解

list:是一个双向带头循环链表,不支持随机访问(即下标访问),任意位置的插入删除效率高。

2、常用接口说明

a.常见的构造函数

list的构造函数
构造函数接口说明
list(size_t n, const T& val = t())构造n个值为val的list
list()构造空的list

list(const list& x)

拷贝构造
list(iterator first, iterator second)用[first, second)构造list

 b.迭代器

迭代器可以看作是一个指向节点的指针。

(rbegin和rend是反向迭代器,rbegin是指向了链表的尾部,rend是指向了链表的头部)

注意:

 begin,进行++操作是往后面走。

rbegin,进行++操作是往前面走。

c. Capacity

d.Element access

e.Modifiers

二、实现

 老规矩,我们还是先对结构进行分析。 

链表:除了一些对链表的基本操作之外,还需要有迭代器和节点。

1、框架 

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

// 开辟一个自己的命名空间
namespace my
{
	// 链表节点
	// 迭代器
	// 链表
}

a.节点

// 链表节点
template<class T>
struct ListNode
{
	ListNode<T>* _prev;
	ListNode<T>* _next;
	T val;

	// 缺省参数,如果没有传参的话,默认是T类型的无参构造
	// 这样做是为了防止T是一个自定义类型
	ListNode(const T& e = T())
		:_prev(nullptr)
		,_next(nullptr)
		,val(e)
	{}
};

b.迭代器

其实这样的迭代器是不完整的,我们在后面会对其进行扩展。

template<class T>
struct NodeIterator
{
    // 对于节点和迭代器的重命名,方便书写
	typedef ListNode<T> Node;// 节点	
	typedef NodeIterator<T> Self; // 迭代器

	Node* _node;
    
    // 迭代器是用节点的指针来进行构造的
	NodeIterator(Node* node)
		:_node(node)
	{}
};

c.链表

// 链表
// list是一个带头双向循环链表
template<class T>
class list
{
	typedef ListNode<T> Node;// 节点
public:
	typedef NodeIterator<T, T&, T*> iterator;// 迭代器
private:
	Node* _head;// 链表的头部
	size_t _size;// 链表的长度
};

2、节点

其实节点这部分没有什么可以讲解的。唯一一个要理解的就是这个(const T& e = T())。

//const T& e = T()
// 首先T()是一个匿名对象的写法
// 为什么我们需要用到一个匿名对象,而不是一个const T& e = 值?
// 1、我们不知道T是什么类型,不知道值可以设成一个什么类型的值
// 2、如果我们设置成一个内置类型,而T是一个自定义类型的话,类型不匹配
ListNode(const T& e = T())
		:_prev(nullptr)
		,_next(nullptr)
		,val(e)
	{}

3、迭代器 

 我之前框架里所写的就是老版本的迭代器。

新版本里面的迭代器主要添加了两个新的模板变量(Ref,Ptr),引入的这两个变量是对解引用和引用的重写。

思考题:怎么写const版本的迭代器呢?

答:新版本写法的迭代器可以直接这么写(以int为例):

NodeIterator<int, const int&, const int*>

思考题:反向迭代器怎么实现的?

标准库里面的begin()和end() 与 rbegin()和rend()采用的是对称法。

// 迭代器
// 新版本
// 链表迭代器:模仿指针的行为
// Ref:引用,Ptr指针
template<class T, class Ref, class Ptr>
struct NodeIterator
{
	typedef ListNode<T> Node;// 节点	
	typedef NodeIterator<T, Ref, Ptr> Self; // 迭代器

	Node* _node;

	NodeIterator(Node* node)
		:_node(node)
	{}
	// 前置++
	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(_node);
		_node = _node->_prev;
		return tmp;
	}
	// 解引用
	Ref operator*()
	{
		return _node->val;
	}
	// -> 
	Ptr operator->()
	{
		return &_node->val;
	}
	// ==
	bool operator==(const Self& s)
	{
		return _node == s._node;
	}
	// !=
	bool operator!=(const Self& s)
	{
		return !(*this == s); 
	}
};
 老版本:缺点:无法实现const的迭代器
 链表迭代器:模仿指针的行为
//template<class T>
//struct NodeIterator
//{
//	typedef ListNode<T> Node;// 节点
//	typedef NodeIterator<T> Self; // 迭代器
//
//	Node* _node;
//
//	NodeIterator(Node* node)
//		:_node(node)
//	{}
//	// 前置++
//	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(_node);
//		_node = _node->_prev;
//		return tmp;
//	}
//	// 解引用
//	T& operator*()
//	{
//		return _node->val;
//	}
//	// ==
//	bool operator==(const Self& s)
//	{
//		return _node == s._node;
//	}
//	// !=
//	bool operator!=(const Self& s)
//	{
//		return !(*this == s); 
//	}
//
//	// -> 
//	T* operator->()
//	{
//		return &_node->val;
//	}
//};

// 反向迭代器
template<class Iterator, class Ref, class Ptr>
struct Reverse_iterator
{
	typedef Reverse_iterator<Iterator, Ref, Ptr> Self;
	Iterator _it;

	Reverse_iterator(Iterator it)
		:_it(it)
	{}

	Ref operator*()
	{
		Iterator tmp(_it);
		return *( -- tmp);
	}
	Ptr operator->()
	{
		//return &_it.operator->();
		return &(_it.operator*());
	}
	Self& operator++()
	{
		_it -- ;
		return *this;
	}
	Self& operator--()
	{
		_it ++;
		return *this;
	}
	bool operator!=(const Self& s)
	{
		return _it != s._it;
	}
};

 

 4、链表

// 链表
template<class T>
class list
{
	typedef ListNode<T> Node;
public:
	typedef NodeIterator<T, T&, T*> iterator;
	typedef NodeIterator<T, const T&, const T*> const_iterator;
	// 构造函数
	list ()
		:_size(0)
	{
		_head = new Node;
		_head -> _next = _head;
		_head -> _prev = _head;
	}
	list (size_t n, const T& val = T())
		:_size(0)
	{
		_head = new Node;
		_head -> _next = _head;
		_head -> _prev = _head;

		for (size_t i = 0; i < n; i ++ )
		{
			push_back(val);
		}
	}
	list (list<T>& x)
		:_size(0)
	{
		_head = new Node;
		_head -> _next = _head;
		_head -> _prev = _head;

		iterator it = x.begin();
		while (it != x.end())
		{
			push_back(*it);
			it ++ ;
		}
	}
	list (iterator first, iterator last)
		:_size(0)
	{
		_head = new Node;
		_head -> _next = _head;
		_head -> _prev = _head;
		iterator it = first;
		while (it != last)
		{
			insert(end(), *it);
			it ++ ;
		}
	}
	~list()
	{
		clear();
		delete _head;
		_head = nullptr;
	}
	// 访问
	// const版本的迭代器,迭代器的指向的内容不能被修改
	// 1、自己单独实现一个const版本的迭代器
	// 2、将const和非const合成一个,通过模板参数进行控制
	const_iterator begin() const
	{
		return _head->_next;
	}
	const_iterator end() const
	{
		return _head;
	}
	iterator begin()
	{
		return iterator(_head->_next);
	}
	iterator end()
	{
		return iterator(_head);
	}
	// list capacity
	bool empty()
	{
		return _size == 0;
	}
	size_t size()
	{
		return _size;
	}
	// list element access
	T& front()
	{
		return *begin();
	}
	T& back()
	{
		return *end();
	}
	// list modifiers
	/*void push_back(const T& val)
	{
	Node* tmp = new Node;
	tmp -> val = val;

	Node* tail = _head -> _prev;
	tmp->_next = tail->_next;
	tail->_next = tmp;
	tmp->_prev = tail;
	_head->_prev = tmp;

	_size ++ ;
	}*/
	void push_back(const T& val)
	{
		insert(end(), val);
	}
	void push_front(const T& val)
	{
		insert(begin(), val);
	}
	iterator insert (iterator position, const T& val)
	{
		Node* tmp = new Node(val);
		tmp->_next = position._node;
		tmp->_prev = position._node->_prev;
		position._node->_prev->_next = tmp;
		position._node->_prev = tmp;

		_size ++ ;
		return iterator(tmp);
	}
	void pop_back()
	{
		erase( -- end());
	}
	void pop_front()
	{
		erase(begin());
	}
	iterator erase (iterator position)
	{
		iterator tmp(position._node->_next);
		position._node->_prev->_next = position._node->_next;
		position._node->_next->_prev = position._node->_prev;
		delete position._node;

		_size -- ;

		return tmp;
	}
	void clear()
	{
		while (_size)
		{
			erase(begin());
		}
	}
	void swap(list& l)
	{
		std::swap(_head, l._head);
		std::swap(_size, l._size);
	}

private:
	Node* _head;
	size_t _size;
};

三、反向迭代器

通过前面例子知道,反向迭代器的++就是正向迭代器的--,反向迭代器的--就是正向迭代器的++,因此反向迭代器的实现可以借助正向迭代器,即:反向迭代器内部可以包含一个正向迭代器,对正向迭代器的接口进行包装即可。

四、问题

1、迭代器中的箭头操作符为什么返回的是地址?

 答:其实是编译器省略了一个箭头,例如:Iterator it;

正常的调用是:it.operator->()->val; 省略了一个箭头是为了提高代码的可读性。

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

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

相关文章

python与java用途区别有哪些

区别&#xff1a; 1.Python比Java简单&#xff0c;学习成本低&#xff0c;开发效率高。 2.Java运行效率高于Python&#xff0c;尤其是纯Python开发的程序&#xff0c;效率极低。 3.Java相关资料多&#xff0c;尤其是中文资料。 4.Java版本比较稳定&#xff0c;Python2和3不…

《动手学深度学习》预备知识和安装环境

哈喽&#xff0c;欢迎来到自学深度学习小白的文章&#xff0c;本文将介绍anacoda是什么和有什么用&#xff0c;以及在win10环境下如何安装运行环境。 关于anaconda 1.环境 准备开始写代码了&#xff0c;教材总是先叫你配好环境&#xff0c;环境可以堪称一栋房子&#xff0c;…

如何给正弦信号添加12V直流偏置

一个有趣问题的探究&#xff1a; 运放在单电源的情况下只能输出正电压&#xff08;单方向的&#xff09;&#xff0c;这就使得有正负值的信号电压只能输出一半&#xff1a; 【单电源供电的运放如何增加直流偏置】&#xff08;电阻分压法&#xff09;&#xff1a; 单电源供电的…

【工具推荐定制开发】一款轻量的批量web请求命令行工具支持全平台:hey,基本安装、配置、使用

背景 在开发 Web 应用的过程中&#xff0c;作为开发人员&#xff0c;为了确认接口的性能能够达到要求&#xff0c;我们往往需要一个接口压测工具&#xff0c;帮助我们快速地对我们所提供的 Web 服务发起批量请求。在接口联调的过程中&#xff0c;我们通常会用 Postman 等图形化…

认识大模型提示词

一、写作助理 &#x1f4a5;最常使用的 prompt&#xff0c;用于优化文本的语法、清晰度和简洁度&#xff0c;提高可读性。 输入&#xff1a;作为一名写作改进助理&#xff0c;你的任务是改进所提供文本的拼写、语法、清晰、简洁和整体可读性&#xff0c;同时分解长句&#xff…

“情况不明,对子先行”攻略

掼蛋作为一种策略性极强的游戏&#xff0c;不仅考验牌技&#xff0c;更考验玩家的智慧和策略布局。这里主要介绍一下当牌力不足的时候的普通策略—情况不明&#xff0c;对子先行。 当你的牌力不强&#xff0c;或者牌局情况不明朗时&#xff0c;自己手上有有比较多的对子&#x…

爱丁堡大学出新招,大小语言模型配合节约成本!

在大语言模型的应用中&#xff0c;往往面临着成本和性能权衡的问题。 一方面&#xff0c;大型LLM的参数量极大&#xff08;如70B&#xff09;&#xff0c;每次调用都可能带来更多的成本&#xff0c;而小型LLM&#xff08;如7B&#xff09;的成本则更容易被接受。 另一方面&am…

【软件测试】用例篇 -- 详解

一、测试用例的基本要素 测试用例&#xff08;Test Case&#xff09;是为了实施测试而向被测试的系统提供的一组集合&#xff0c;这组集合包含&#xff1a;测试环境、操作步骤、测试数据、预期结果等要素。&#xff08;注意&#xff1a;不需要执行结果&#xff0c;因为执行结果…

四、Redis五种常用数据类型-List

List是Redis中的列表&#xff0c;按照插入顺序保存数据&#xff0c;插入顺序是什么样的&#xff0c;数据就怎么保存。可以添加一个元素到列表的头部(左边)或者尾部(右边)。一个列表最多可以包含232-1个元素(4294967295&#xff0c;每个列表超过40亿个元素)。是一种双向列表结构…

如何获取中国各省市区的边界

前几个专栏我介绍了获取各流域边界的方法&#xff0c;可参见以下的文章&#xff1a; 格林兰岛和南极洲的流域边界文件下载-CSDN博客 读取shp文件中的经纬度坐标-CSDN博客 读取谷歌地球的kml文件中的经纬度坐标_谷歌地球识别穿过矿区的公路,并获取公路的经纬度坐标-CSDN博客 关于…

图像处理:图像噪声添加

文章目录 前言一、高斯噪声二、椒盐噪声三、泊松噪声四、斑点噪声五、指数噪声六、均匀噪声总结 前言 本文主要介绍几种添加图像噪声的方法&#xff0c;用于数据增强等操作。 以下图为例。 一、高斯噪声 高斯噪声就是给图片添加一个服从高斯分布的噪声&#xff0c;可以通过调…

OpenGL 入门(三)—— OpenGL 与 OpenCV 共同打造大眼滤镜

从本篇开始&#xff0c;会在上一篇搭建的滤镜框架的基础上&#xff0c;介绍具体的滤镜效果该如何制作。本篇会先介绍大眼滤镜&#xff0c;先来看一下效果&#xff0c;原图如下&#xff1a; 使用手机后置摄像头对眼部放大后的效果&#xff1a; 制作大眼滤镜所需的主要知识点&…

Qt应用开发(拓展篇)——图表 QChart

一、前言 QChart是一个图形库模块&#xff0c;它可以实现不同类型的序列和其他图表相关对象(如图例和轴)的图形表示。要在布局中简单地显示图表&#xff0c;可以使用QChartView来代替QChart。此外&#xff0c;线条、样条、面积和散点序列可以通过使用QPolarChart类表示为极坐标…

PRL:新型量子传感方案突破纳米测量极限

朴茨茅斯大学的研究人员近期宣布了一项令人振奋的量子传感方案&#xff0c;该方案在测量两个干涉光子之间的横向位移方面达到了前所未有的量子灵敏度。 这一技术的突破为超分辨率成像技术带来了新的可能性。目前&#xff0c;这些技术通常采用单光子源作为探针&#xff0c;用于在…

无刷电机和有刷电机的区别

无刷电机和有刷电机的区别 无刷电机的定子上绕着线圈&#xff0c;线圈通常是成对出现的&#xff0c;通过控制电路为每一对线圈按照一定顺序输入电流&#xff0c;就可以产生旋转的磁场 它还有一个永磁体转子&#xff0c;现在多采用高磁能级的稀土铷铁硼材料&#xff0c;体积更小…

Python练习(函数)

目录 6-1 使用函数求素数和 函数接口定义&#xff1a; 裁判测试程序样例&#xff1a; 输入样例&#xff1a; 输出样例&#xff1a; 6-2 使用函数输出指定范围内Fibonacci数的个数 函数接口定义&#xff1a; 裁判测试程序样例&#xff1a; 输入样例&#xff1a; 输出样…

【AI绘画】Midjourney 工笔画 水蓝色衣服的少女

using Midjourney 提示词&#xff1a; highly detailed,细节刻画细腻,超高清晰度,32k,HD,大师作品,高质量,动漫少女,水墨人像,20岁年轻身材很好的中国少女,惊人的美貌,五官精致,精致的妆容,华丽的水蓝色衣服,古风服饰,华丽的珠宝,飞扬的黑色长发,大风吹起头发,宝石发光,黄金装饰…

tf2使用savemodel保存之后转化为onnx适合进行om模型部署

tf2使用savemodel保存之后转化为onnx适合进行om模型部署 tf保存为kears框架h5文件将h5转化为savemodel格式&#xff0c;方便部署查看模型架构将savemodel转化为onnx格式使用netrononnx模型细微处理代码转化为om以及推理代码&#xff0c;要么使用midstudio tf保存为kears框架h5文…

Windows注册表

注册表 一.概述 注册表&#xff08;Registry&#xff09;是Microsoft Windows中的一个重要的数据库&#xff0c;用于[存储系统]和[应用程序]的设置信息。早在[Windows 3.0]推出[OLE]技术的时候&#xff0c;注册表就已经出现。随后推出的[Windows NT]是第一个从系统级别广泛使…

IT项目管理 选择/判断 【太原理工大学】

第一章、IT项目管理 判断题 1、搬家属于项目。&#xff08; 对 &#xff09; 2、项目是为了创造一个唯一的产品或提供一个唯一的服务而进行的永久性的努力。&#xff08; 错 &#xff09; 3、项目具有临时性的特征。&#xff08; 对 &#xff09; 4、项目开发过程…