C++----剖析list

news2025/6/9 5:55:16

前面学习了vector和string,接下来剖析stl中的list,在数据库中学习过,list逻辑上是连续的,但是存储中是分散的,这是与vector这种数组类型不同的地方。所以list中的元素设置为一个结构体,将list设计成双向的:

	template <class T>
	struct list_node
	{
		list_node<T>* _next;
		list_node<T>* _pre;
		T _data;

		list_node(const T& val=T())
			:_next(nullptr),
			_pre(nullptr),
			_data(val)
		{

		}
	};

接下来是stl中的迭代器部分,list的迭代器还可以像vector这种原生指针是的使用方式使用吗?答案是否定的,因为vector的底层存储是连续的,并且迭代器就是vector内部元素指针的简单封装。所以底层自然实现起来比较简单,但是list的底层不可以,比如迭代器++,vector本来就是连续的,所以直接就能用,但是struct不是连续的,所以要自己实现,否则谁知道会++到哪里造成访问未知空间。所以既然它不支持我们直接使用,那我们就造一个迭代器出来,这个迭代器的核心就是node:

	template <class T, class PTR, class PEF>
	struct _list_iterator
	{
		typedef list_node<T> Node;
		typedef _list_iterator<T,PTR,PEF> self;
		Node* _node;//核心

		_list_iterator(Node* node)
			:_node(node)
		{}

		PEF operator*()
		{
			return _node->_data;
		}//要可以返回值、还能修改!

		PTR operator->()
		{
			return &(operator*());
		}

		self& operator++()
		{
			_node = _node->_next;
			return *this;
		}
		//后置++
		self operator++(int)
		{
			self tmp(*this);
			_node = _node->_next;
			return tmp;
		}

		self& operator--()
		{
			_node = _node->_pre;
			return *this;
		}
		//后置--
		self operator--(int)
		{
			self tmp(*this);
			_node = _node->_pre;
			return tmp;
		}

		bool operator!=(const self& it)
		{
			return _node != it._node;
		}

		bool operator==(const self& it)
		{
			return _node == it._node;
		}


	};

这里其他的地方没有太大的疑问,就是operator->发现了,返回的是nodedata的地址,首先咱们来看,当T是内置类型int时,是没有必要用这个的吧?直接*就可以得到,但是如果T时自定义类型的呢?比如:

	struct A
	{
		int _a;
		int _b;
		A(int a = 1,int b=1)
			:_a(a)
			,_b(b)
		{}
	};

	void test2()
	{
		list<A> l;
		l.push_back(A(1,1));
		l.push_back(A(2, 2));
		l.push_back(A(3, 3));
		l.push_back(A(4, 4));
        std::cout << it->_a << " " << it->_b << " ";
		list<A>::iterator it = l.begin();
	}

此时我想访问A中的数据,我当然可以重载<<这个运算符了,但是在stl中我们不知道自定义类型中会有什么数据,所以stl中是不会预先,也不能写出来<<运算符。所以就有了->符,我们可以自己访问struct中的数据,但是还有一个问题,->返回struct的地址,还应该再来一个->吧,就是it->->_a,这里编译器做了优化,所以只需要一个->就可以。

看list:

	template <class T>
	class list
	{
		typedef list_node<T> Node;
		
	public:
		typedef _list_iterator<T,T*,T&> iterator;
		typedef _list_iterator<T,const T*,const T&> const_iterator;


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

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

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

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

		list()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_pre = _head;
		}

		//void push_back(Node newnode)
		//{
		//	Node* tail = _head->_pre;
		//	tail->_next = newnode;
		//	newnode->_pre = tail;
		//	newnode->_next = _head;
		//	_head->_pre = newnode;
		//}

		void push_back(const T& val)
		{
			Node* newnode = new Node(val);
			Node* tail = _head->_pre;
			tail->_next = newnode;
			newnode->_pre = tail;
			newnode->_next = _head;
			_head->_pre = newnode;
		}
	private:
		Node* _head;
	};

list的形式:带哨兵位的双向列表

解释模板在const迭代器和普通迭代器中的巧妙使用,通过模板避免多写代码:

 巧妙使用模板参数。

list迭代器失效的场景只存在于删除结点时,他不像vector这种连续的,插入了会导致其中大部分元素发生移动,所以只会在删除结点时,如何解决?返回删除结点的下一个节点的迭代器。

附全部代码:

	template <class T>
	struct list_node
	{
		list_node<T>* _next;
		list_node<T>* _pre;
		T _data;

		list_node(const T& val=T())
			:_next(nullptr),
			_pre(nullptr),
			_data(val)
		{

		}
	};

	template <class T, class PTR, class PEF>
	struct _list_iterator
	{
		typedef list_node<T> Node;
		typedef _list_iterator<T,PTR,PEF> self;
		Node* _node;//核心

		_list_iterator(Node* node)
			:_node(node)
		{}

		PEF operator*()
		{
			return _node->_data;
		}//要可以返回值、还能修改!

		PTR operator->()
		{
			return &(operator*());
		}

		self& operator++()
		{
			_node = _node->_next;
			return *this;
		}
		//后置++
		self operator++(int)
		{
			self tmp(*this);
			_node = _node->_next;
			return tmp;
		}

		self& operator--()
		{
			_node = _node->_pre;
			return *this;
		}
		//后置--
		self operator--(int)
		{
			self tmp(*this);
			_node = _node->_pre;
			return tmp;
		}

		bool operator!=(const self& it)
		{
			return _node != it._node;
		}

		bool operator==(const self& it)
		{
			return _node == it._node;
		}


	};

	template <class T>
	class list
	{
		typedef list_node<T> Node;
		
	public:
		typedef _list_iterator<T,T*,T&> iterator;
		typedef _list_iterator<T,const T*,const T&> const_iterator;


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

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

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

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

		list()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_pre = _head;
		}

		void empty_init()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_pre = _head;
		}

		template<class InputIterator>
		list(InputIterator first, InputIterator last)
		{
			empty_init();

			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		} 

		void swap(list<T>& l)
		{
			std::swap(_head, l._head);
		}
		  
		list(const list<T>& l)
		{ 
			empty_init();
			list<T> tmp(l.begin(), l.end());
			swap(tmp);
		}

		//l2=l1 
		list<T>& operator=(list<T> l)
		{
			swap(l);
			return *this;
		}

		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}
		}

		//void push_back(Node newnode)
		//{
		//	Node* tail = _head->_pre;
		//	tail->_next = newnode;
		//	newnode->_pre = tail;
		//	newnode->_next = _head;
		//	_head->_pre = newnode;
		//}

		void insert(iterator pos, const T& val)
		{
			Node* newnode = new Node(val);
			Node* cur = pos._node;
			Node* pre = cur->_pre;

			cur->_pre = newnode;
			newnode->_next = cur;
			pre->_next = newnode;
			newnode->_pre = pre;
		}

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

		void push_back(const T& val)
		{
			Node* newnode = new Node(val);
			Node* tail = _head->_pre;
			tail->_next = newnode;
			newnode->_pre = tail;
			newnode->_next = _head;
			_head->_pre = newnode;
		}

		//返回值是删去节点的下一个节点的迭代器,避免迭代器失效,
		//因为释放了当前迭代器的结点,再次访问会出现错误
		iterator erase(iterator pos)
		{
			assert(pos != end());
			Node* cur = pos._node;
			Node* pre = cur->_pre;
			Node* next = cur->_next;

			next->_pre = pre;
			pre->_next = next;
			delete cur;

			return iterator(next);
		}

		void pop_front()
		{
			iterator it = erase(begin());

		}

		void pop_back()
		{
			iterator it = erase(--end());

		}
	private:
		Node* _head;
	};

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

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

相关文章

纳米AI搜索与百度AI搜、豆包的核心差异解析

一、技术定位与设计目标 1、纳米AI搜索&#xff1a;轻量化边缘计算导向
专注于实时数据处理与资源受限环境下的高效响应&#xff0c;通过算法优化和模型压缩技术&#xff0c;实现在物联网设备、智能终端等低功耗场景的本地化部署。其核心优势在于减少云端依赖&#xff0c;保障…

不到 2 个月,OpenAI 火速用 Rust 重写 AI 编程工具。尤雨溪也觉得 Rust 香!

一、OpenAI 用 Rust 重写 Codex CLI OpenAI 已用 Rust 语言重写了其 AI 命令行编程工具 Codex CLI&#xff0c;理由是此举能提升性能和安全性&#xff0c;同时避免对 Node.js 的依赖。他们认为 Node.js “可能让部分用户感到沮丧或成为使用障碍”。 Codex 是一款实验性编程代理…

Python60日基础学习打卡Day46

一、 什么是注意力 注意力机制的由来本质是从onehot-elmo-selfattention-encoder-bert这就是一条不断提取特征的路。各有各的特点&#xff0c;也可以说由弱到强。 其中注意力机制是一种让模型学会「选择性关注重要信息」的特征提取器&#xff0c;就像人类视觉会自动忽略背景&…

WEB3全栈开发——面试专业技能点P1Node.js / Web3.js / Ethers.js

一、Node.js 事件循环 Node.js 的事件循环&#xff08;Event Loop&#xff09;是其异步编程的核心机制&#xff0c;它使得 Node.js 可以在单线程中实现非阻塞 I/O 操作。 &#x1f501; 简要原理 Node.js 是基于 libuv 实现的&#xff0c;它使用事件循环来处理非阻塞操作。事件…

Vscode下Go语言环境配置

前言 本文介绍了vscode下Go语言开发环境的快速配置&#xff0c;为新手小白快速上手Go语言提供帮助。 1.下载官方Vscode 这步比较基础&#xff0c;已经安装好的同学可以直接快进到第二步 官方安装包地址&#xff1a;https://code.visualstudio.com/ 双击一直点击下一步即可,记…

Go语言--语法基础5--基本数据类型--输入输出(1)

I : input 输入操作 格式化输入 scanf O &#xff1a; output 输出操作 格式化输出 printf 标准输入 》键盘设备 》 Stdin 标准输出 》显示器终端 》 Stdout 异常输出 》显示器终端 》 Stderr 1 、输入语句 Go 语言的标准输出流在打印到屏幕时有些参数跟别的语言…

永磁同步电机无速度算法--自适应龙贝格观测器

一、原理介绍 传统龙伯格观测器&#xff0c;在设计观测器反馈增益矩阵K时&#xff0c;为简化分析与设计&#xff0c;根据静止两相坐标系下的对称关系&#xff0c;只引入了K、K,两个常系数&#xff0c;且在实际应用时&#xff0c;大多是通过试凑找到一组合适的反馈增益系数缺乏…

LangChain工具集成实战:构建智能问答系统完整指南

导读&#xff1a;在人工智能快速发展的今天&#xff0c;如何构建一个既能理解自然语言又能调用外部工具的智能问答系统&#xff0c;成为许多开发者面临的核心挑战。本文将为您提供一套完整的解决方案&#xff0c;从LangChain内置工具包的基础架构到复杂系统的工程实践。 文章深…

【razor】x264 在 的intra-refresh和IDR插帧

你提到的是这样一个情况: 使用 DirectShow 采集,帧率稳定(如回调了20帧)使用 x264 的 total intra refresh 模式(intra-refresh=1) 进行编码但编码过程中「隔几十秒才有一帧intra(关键帧)」这不正常,具体分析如下: 🎯 一、问题核心 x264 的 intra refresh 模式(特…

随机算法一文深度全解

随机算法一文深度全解 一、随机算法基础1.1 定义与核心特性1.2 算法优势与局限 二、随机算法经典案例2.1 随机化快速排序原理推导问题分析与策略代码实现&#xff08;Python、Java、C&#xff09; 2.2 蒙特卡罗方法计算 π 值原理推导问题分析与策略代码实现&#xff08;Python…

在 Conda 环境下配置 Jupyter Notebook 环境和工作目录

作为数据科学家或Python开发者&#xff0c;Jupyter Notebook 是我们日常工作的得力工具。本文将详细介绍如何在 Conda 环境中配置 Jupyter Notebook&#xff0c;包括环境设置和工作目录管理&#xff0c;帮助你打造高效的工作流程。 为什么要在 Conda 环境中使用 Jupyter Noteb…

MS39531N 是一款正弦驱动的三相无感直流电机驱动器,具有最小振动和高效率的特点

MS39531N 是一款正弦驱动的三相无感直流电机驱动器&#xff0c;具有最小振动和高效率的特点 简述 MS39531 是一款正弦驱动的 三相无感直流电机驱动器 &#xff0c;具有最小振动和高效率的特点。该驱动器内部集成了基本的闭环速度控制功能&#xff0c;能够根据特定的应用定制电…

web3-基于贝尔曼福特算法(Bellman-Ford )与 SMT 的 Web3 DeFi 套利策略研究

web3-基于贝尔曼福特算法&#xff08;Bellman-Ford &#xff09;与 SMT 的 Web3 DeFi 套利策略研究 如何找到Defi中的交易机会 把defi看做是一个完全开放的金融产品图表&#xff0c;可以看到所有的一切东西&#xff1b;我们要沿着这些金融图表找到一些最优的路径&#xff0c;就…

分析 java 的 Map<String,Map<String, List<Map<String,Integer>>>>

import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map;public class Test02 {public static void main(String[] args) {//分析方法&#xff1a;由外层向内层逐渐拆解要定义的变量。再由内向外进行变量赋值//外层第一层&#x…

ChatterBox - 轻巧快速的语音克隆与文本转语音模型,支持情感控制 支持50系显卡 一键整合包下载

ChatterBox 是一个近期备受关注的开源语音克隆与文本转语音&#xff08;TTS&#xff09;模型&#xff0c;由 Resemble AI 推出&#xff0c;具备体积轻巧及超快的推理速度等特色。它也是首个支持情感夸张控制的开放源代码 TTS 模型&#xff0c;这一强大功能能让您的声音脱颖而出…

前端开发面试题总结-HTML篇

文章目录 HTML面试高频问答一、HTML 的 src 和 href 属性有什么区别?二、什么是 HTML 语义化?三、HTML的 script 标签中 defer 和 async 有什么区别?四、HTML5 相比于 HTML有哪些更新?五、HTML行内元素有哪些? 块级元素有哪些? 空(void)元素有哪些?六、iframe有哪些优点…

嵌入式学习--江协stm32day4

只能说拖延没有什么好结果&#xff0c;欠下的债总是要还的。 ADC 模拟信号转化为数字信号&#xff0c;例如温度传感器将外部温度的变化&#xff08;模拟信号&#xff09;&#xff0c;转换为内部电压的变化&#xff08;数字信号&#xff09; IN是八路输入&#xff0c;下方是选择…

【Matlab】连接SQL Server 全过程

文章目录 一、下载与安装1.1 SQL Server1.2 SSMS1.3 OLE DB 驱动程序 二、数据库配置2.1 SSMS2.2 SQL Server里面设置2.3 设置防火墙2.4 设置ODBC数据源 三、matlab 链接测试 一、下载与安装 微软的&#xff0c;所以直接去微软官方下载即可。 1.1 SQL Server 下载最免费的Ex…

9.RV1126-OPENCV 视频的膨胀和腐蚀

一.膨胀 1.视频流的膨胀流程 之前膨胀都是在图片中进行的&#xff0c;现在要在视频中进行也简单&#xff0c;大概思路就是&#xff1a;获取VI数据&#xff0c;然后把VI数据给Mat化发给VENC模块&#xff0c;然后VENC模块获取&#xff0c;这样就完成了。流程图&#xff1a; 2.代…

华为OD机考-内存冷热标记-多条件排序

import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextInt();int[] arr new int[a];for(int…