【STL】list的底层原理及其实现

news2025/1/24 5:39:15

文章目录

  • list的介绍
  • list的整体结构设计
  • list的构造
    • 代码模拟实现:
  • list节点类的实现
  • list 迭代器Iterator的使用以及实现
    • Iterator的使用
    • Iterator的底层实现
    • 反向迭代器
  • list与vector的比较
  • 实现list类

list的介绍

  1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
  2. list的底层是用双向链表实现的(线性),每个元素都存在相互独立的节点中,每个节点都有一个指针分别指向前一个节点和后一个节点。
  3. 因为底层结构是链表,list的插入和删除操作是非常高效的,这与vector容器相反。但是由于链表的结构特点,list的各个节点之间的物理地址是不连续的,也就导致了任意访问某个节点的效率较低
  4. list的空间消耗会比vector大(存储相同个数的元素),因为每个节点还需要给前后指针开空间。

在这里插入图片描述

list的整体结构设计

list可以分为三部分:一个是list类本身,一个是节点类,一个是迭代器类。

list类的成员变量一般只有头节点(哨兵),除了负责初始化以外还负责声明和定义插入删除功能。
ListNode节点类封装了list的元素以及前后节点的指针,还负责new出节点时的初始化。
Iterator迭代器类封装了指向节点的指针ListNode*,还负责重载++、–、!=等运算符。为什么要在迭代器内部重载呢?

跟vector不同的是,由于list迭代器指向的是一个节点,且节点间物理地址不连续,向前移动或者向后移动都不能用指针直接去加减。

在这里插入图片描述
list的节点类

 // List的节点类
 template<class T>
 struct ListNode
 {
     ListNode(const T& val = T());
     ListNode<T>* _pPre;
     ListNode<T>* _pNext;
     T _val;
 };

list的迭代器类

template<class T, class Ref, class Ptr>
class ListIterator
{
    typedef ListNode<T>* PNode;
    typedef ListIterator<T, Ref, Ptr> Self;
public:
    ListIterator(PNode pNode = nullptr);
    ListIterator(const Self& l);
    T& operator*();
    T* operator->();
    Self& operator++();
    Self operator++(int);
    Self& operator--();
    Self& operator--(int);
    bool operator!=(const Self& l);
    bool operator==(const Self& l);
private:
    PNode _pNode;
};

list类

template<class T>
class list
{
    typedef ListNode<T> Node;
    typedef Node* PNode;
public:
    typedef ListIterator<T, T&, T*> iterator;
    typedef ListIterator<T, const T&, const T&> const_iterator;
public:
    ///
    // List的构造
    list();
    list(int n, const T& value = T());
    template <class Iterator>
    list(Iterator first, Iterator last);
    list(const list<T>& l);
    list<T>& operator=(const list<T> l);
    ~list();

    ///
    // List Iterator
    iterator begin();
    iterator end();
    const_iterator begin();
    const_iterator end();
    ///
    // List Capacity
    size_t size()const;
    bool empty()const;

};

list的构造

list有四个构造函数:无参构造、拷贝构造、连续赋值构造、迭代器构造。

//构造的list中包含n个值为val的元素
list (size_type n, const value_type& val = value_type())
//构造空的list,初始化哨兵节点
list() 
//拷贝构造
list (const list& x)
//用[first, last)区间中的元素构造list
list (InputIterator first, InputIterator last) 

代码模拟实现:

		void Empty_Init() {
			head = new Node;
			head->_pre = head;
			head->_next = head;
			head->_val = -1;
			sz = 0;
		}
		//构造
		list()
		{
			Empty_Init();
		}
		list(size_t n, const T& value = T())
		{
			Empty_Init();
			for (size_t i = 0; i < n; i++) {
				push_back(value);
			}
			sz = n;
		}
		//拷贝构造
		list(const list<T>& lt) {
			Empty_Init();
			for (auto& it : lt) {
				push_back(it);
			}
		}
		//迭代器构造
		template <class IIterator>
		list(IIterator first, IIterator last) {
			Empty_Init();
			while (first != last) {
				push_back(*first);
				first++;
			}
		}

list节点类的实现

节点类的成员变量就是前后指针以及节点的元素值。此外还需注意构造节点时的初始化工作。

template<class T>
class ListNode {
public:
	ListNode(const T& val = T())
		:_val(val)
		, _pre(nullptr)
		, _next(nullptr)
	{

	}
	ListNode<T>* _pre;
	ListNode<T>* _next;
	T _val;
};

list 迭代器Iterator的使用以及实现

Iterator的使用

在上面iterator类的声明中我们可以看到,不同的容器其迭代器的实现方式是不一样的。在string和vector中的iterator本质是一个指针。但是list的迭代器是一个像指针的类

//返回第一个元素或最后一个的迭代器
begin()
end()

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

在这里插入图片描述

  1. begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
  2. rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动
    代码演示:
    在这里插入图片描述

Iterator的底层实现

先实现一个正向的迭代器类。因为涉及到存在const修饰迭代器指向的节点,所以在设计迭代器的时候需要同时设计出const版本(const是修饰节点,不是迭代器本身)。这里我们可以使用模板,模板里面有三个类型:节点数据类型、引用类型、指针类型
在这里插入图片描述
值得注意的是,由于我们需要在迭代器类里访问节点Node类型的成员变量,所以可以将Iterator设为Node类的友元类,或者开放Node类的权限。

迭代器的构造函数的实现:

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

我这里设计的比较简单,只实现了单参的构造函数,可以支持基本的隐式类型转换。

重载*:

Ref operator* () {
	return _node->_val;
}

重载迭代器的移动操作符++--:

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

Self& operator--() {
	_node = _node->_pre;
	return *this;
}

Self& operator--(int) {//后置--
	Self temp(_node);
	_node = _node->_pre;
	return temp;
}

重载->

Ptr operator ->() {
	return &_node->_val;
}

由于我们想把迭代器当指针使用,重载->是必要的,那么为什么是返回节点元素的地址呢?其实当我们在使用迭代器->时,编译器会自动优化成->->。比如我们的节点元素类型是一个类,我们有时需要访问节点元素类中的成员变量,此时希望通过迭代器->能直接访问到。
观察以下代码:
在这里插入图片描述
其中t是一个结构体类型,当我们用list存这样的节点并试图遍历节点中的a1的值时,(*it)得到的是t类型,如果我们想要输出t中的a1,就必须写成(*it).a1
我们希望迭代器能像指针一样,it->a1这样就能直接访问a1的元素,于是我们重载一个->,这个->的作用其实等价于it->->a1也就是it.operator->()->a1。这是编译器帮我们优化了。
在这里插入图片描述

反向迭代器

方向迭代器和普通的迭代器功能基本一致,只不过由于起始位置是在哨兵位,解引用时需要先往前面移动一个节点再返回其节点元素值

Ref operator* () {
	Self temp(_node);
	temp++;
	return temp._node->_val;
}

此外移动的方向也和普通迭代器相反,是向节点的前一个节点方向移动。

	Self& operator--() {
		_node = _node->_next;
		return *this;
	}
	Self& operator--(int) {//后置--
		Self temp(*this);
		_node = _node->_next;
		return temp;
	}

	Self& operator++() {
		_node = _node->_pre;
		return *this;
	}

	Self& operator++(int) {//后置++
		Self temp(_node);
		_node = _node->_pre;
		return temp;
	}

其它功能和普通迭代器几乎一致。

list与vector的比较

list和vector各种代表着的是链表和数组。它们之间的具体区别其实在前面已经讲过了。
链表的优缺点
顺序表的优缺点
迭代器的区别:

vector的迭代器是原生态指针,list的迭代器是对原生态指针(节点指针)进行封装
vector在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效,删除时,当前迭代器需要重新赋值否则会失效。
而list插入元素不会导致迭代器失效,删除元素时,只会导致当前迭代器失效,其他迭代器不受影响

实现list类

#define _CRT_SECURE_NO_WARNINGS 1
#include<assert.h>
#include<iostream>

namespace bite {

	//节点
	template<class T>
	class ListNode {
	public:
		ListNode(const T& val = T())
			:_val(val)
			, _pre(nullptr)
			, _next(nullptr)
		{

		}
		ListNode<T>* _pre;
		ListNode<T>* _next;
		T _val;
	};


	//反向迭代器
	template<class T, class Ref, class Ptr>
	class ReserveListIterator {
	public:
		typedef ListNode<T> Node;
		typedef ReserveListIterator<T, Ref, Ptr> Self;

		ReserveListIterator(Node* node)
			:_node(node)
		{}
		//重载
		Ref operator* () {
			Self temp(_node);
			temp++;
			return temp._node->_val;
		}

		Self& operator--() {
			_node = _node->_next;
			return *this;
		}
		Self& operator--(int) {//后置--
			Self temp(*this);
			_node = _node->_next;
			return temp;
		}

		Self& operator++() {
			_node = _node->_pre;
			return *this;
		}

		Self& operator++(int) {//后置++
			Self temp(_node);
			_node = _node->_pre;
			return temp;
		}

		bool operator!=(const Self& p) {
			return _node != p._node;
		}
		//T*/const T*
		Ptr operator ->() {
			return &_node->_val;
		}
		bool operator==(const Self& p) {
			return _node == p._node;
		}
		Node* _node;
	};


	//迭代器
	template<class T, class Ref, class Ptr>//T表示节点数据类型,Ref表示T&、Ptr表示T*类型
	class ListIterator {
	public:
		typedef ListNode<T> Node;
		typedef ListIterator<T, Ref, Ptr> Self;

		ListIterator(Node* node)
			:_node(node)
		{}
		//重载
		Ref operator* () {
			return _node->_val;
		}

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

		Self& operator--() {
			_node = _node->_pre;
			return *this;
		}

		Self& operator--(int) {//后置--
			Self temp(_node);
			_node = _node->_pre;
			return temp;
		}

		bool operator!=(const Self& p) {
			return _node != p._node;
		}
		//T*/const T*
		Ptr operator ->() {
			return &_node->_val;
		}
		bool operator==(const Self& p) {
			return _node == p._node;
		}
		Node* _node;
	};

	template<class T>
	class list {
	public:
		//节点
		typedef ListNode<T> Node;
		typedef Node* pNode;

		//迭代器
		typedef ListIterator<T, T&, T*> Iterator;
		typedef ListIterator<T, const T&, const T*> const_Iterator;
		typedef ReserveListIterator<T, T&, T*> Reserve_Iterator;
		typedef ReserveListIterator<T, const T&, const T*> const_Reserve_Iterator;
		

	public:

		void Empty_Init() {
			head = new Node;
			head->_pre = head;
			head->_next = head;
			head->_val = -1;
			sz = 0;
		}

		//构造
		list()
		{
			Empty_Init();
		}

		list(size_t n, const T& value = T())
		{
			Empty_Init();
			for (size_t i = 0; i < n; i++) {
				push_back(value);
			}
			sz = n;
		}

		//拷贝构造
		list(const list<T>& lt) {
			Empty_Init();
			for (auto& it : lt) {
				push_back(it);
			}
		}

		//迭代器构造
		template <class IIterator>
		list(IIterator first, IIterator last) {
			Empty_Init();
			while (first != last) {
				push_back(*first);
				first++;
			}
		}

		//析构
		~list() {
			Iterator it = begin();
			while (it != end()) {
				it = erase(it);
			}
			delete head;
			sz = 0;
		}

		void swap(list<T> lt) {
			std::swap(lt.head, head);
			std::swap(sz, lt.sz);
		}


		//普通迭代器
		Iterator begin() {
			return head->_next;
		}
		Iterator end() {
			return head;
		}
		//const迭代器
		const_Iterator begin() const {
			return head->_next;
		}
		const_Iterator end() const {
			return head;
		}
		//反向迭代器
		 Reserve_Iterator  rbegin() {
			 return head;
		 }
		 Reserve_Iterator  rend() {
			 return head->_next;
		 }

		 const_Reserve_Iterator rbegin() const {
			 return head;
		 }
		 const_Reserve_Iterator rend() const {
			 return head->_next;
		 }

		//插入
		void insert(Iterator pos, const T& val) {
			Node* newnode = new Node(val);
			Node* cur = pos._node;

			newnode->_pre = cur->_pre;
			newnode->_next = cur;
			cur->_pre->_next = newnode;
			cur->_pre = newnode;
			sz++;
		}
		//删除
		Iterator erase(Iterator pos) {
			assert(sz > 0);
			Node* cur = pos._node;
			Node* t = cur->_next;
			cur->_pre->_next = cur->_next;
			cur->_next->_pre = cur->_pre;
			delete cur;
			sz--;
			return t;
		}
		//尾插
		void push_back(const T& val) {
			insert(end(), val);
			//size++;
		}


		//操作符重载
		list<T>& operator = (list<T> lt) {
			swap(lt);
			return *this;
		}


		// List Capacity
		size_t size()const {
			return sz;
		}
		bool empty()const {
			return sz == 0;
		}

	private:
		pNode head;
		size_t sz;
	};
}

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

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

相关文章

springCloud-LoadBalancer负载均衡微服务负载均衡器LoadBalancer

2020年前SpringCloud是采用Ribbon作为负载均衡实现&#xff0c;但是在2020后采用了LoadBalancer替代 LoadBalancer默认提供了两种负载均衡策略&#xff08;只能通过配置类来修改负载均衡策略&#xff09; 1.RandomLoadBalancer-随机分配策略 2.RoundRobinLoadBalancer-轮询分配…

【WSN覆盖优化】基于灰狼优化算法的无线传感器网络覆盖 GWO-WSN覆盖优化【Matlab代码#74】

文章目录 【可更换其他算法&#xff0c;获取资源请见文章第5节&#xff1a;资源获取】1. 灰狼优化算法2. WSN节点感知模型3. 部分代码展示4. 仿真结果展示5. 资源获取 【可更换其他算法&#xff0c;获取资源请见文章第5节&#xff1a;资源获取】 1. 灰狼优化算法 此处略。 2.…

尚硅谷html5+css3(1)html相关知识

1.基本标签&#xff1a; <h1>最大的标题字号 <h2>二号标题字号 <p>换行 2.根标签<html> 包括<head>和<body> <html><head><title>title</title><body>body</body></head> </html> 3…

Golang单元测试和压力测试

一.单元测试 1.1 go test工具 go语言中的测试依赖go test命令。编写测试代码和编写普通的Go代码过程类似&#xff0c;并不需要学习新的语法&#xff0c;规则和工具。 go test命令是一个按照一定约定和组织的测试代码的驱动程序。在包目录内&#xff0c;所有以_test.go为后缀名的…

2.网络编程-HTTP和HTTPS

目录 HTTP介绍 HTTP协议主要组成部分 GET 和 POST有什么区别 常见的 HTTP 状态码有哪些 http状态码100 HTTP1.1 和 HTTP1.0 的区别有哪些 HTTPS 和 HTTP 的区别是什么 HTTP2 和 HTTP1.1 的区别是什么 HTTP3 和 HTTP2 的区别是什么 HTTPS的请求过程 对称加密和非对称…

SVG图标显示

SVG图标显示 1.安装SharpVectors.Wpf包 2.添加引用 xmlns:svgc"http://sharpvectors.codeplex.com/svgc/"3.加载svg文件&#xff0c;生成操作选择资源(Resource) 4.UI界面显示SVG图像 <Button Click"OnSaveFileClick" ToolTip"Save Svg File…

云原生安全当前的挑战与解决办法

云原生安全作为一种新兴的安全理念&#xff0c;不仅解决云计算普及带来的安全问题&#xff0c;更强调以原生的思维构建云上安全建设、部署与应用&#xff0c;推动安全与云计算深度融合。所以现在云原生安全在云安全领域越来受到重视&#xff0c;云安全厂商在这块的投入也是越来…

蓝桥杯—PCF8951

1.整个系统靠SDA和SCL实现完善的全双工数据传输 2.引脚图 AN1为光明电阻 AN3为滑动变阻 A0-A2均接地 时钟线连P20 地址线连P21 实物图 iic总线 谁控制时钟线谁是主设备 时序相关 官方提供的底层驱动代码 /* # I2C代码片段说明1. 本文件夹中提供的驱动代码供参赛选手完成…

Octopus V2:设备端super agent的高级语言模型

论文&#xff1a;Octopus v2: On-device language model for super agent论文地址&#xff1a;https://arxiv.org/abs/2404.01744模型主页&#xff1a;https://huggingface.co/NexaAIDev/Octopus-v2 Octopus-V2-2B Octopus-V2-2B 是一款具有20亿参数的开源先进语言模型&#…

SQL Sever 2008 安装教程

先从官网下载程序&#xff1a;下载地址 打开上述链接后&#xff0c;点击下载按钮。 就会跳出下面这个界面&#xff0c;如果你的电脑是64位的请选择下图中这两个程序。 下载完成后&#xff0c;在电脑磁盘中找到这两个文件&#xff0c;注意安装的顺序&#xff0c;先安装 SQLEXPR…

校园圈子小程序,大学校园圈子,三段交付,源码交付,支持二开

介绍 在当今的数字化时代&#xff0c;校园社交媒体和在线论坛成为了学生交流思想、讨论问题以及分享信息的常用平台。特别是微信小程序&#xff0c;因其便捷性、用户基数庞大等特点&#xff0c;已逐渐成为构建校园社区不可或缺的一部分。以下是基于现有资料的校园小程序帖子发…

蓝桥杯每日一题:杨辉三角形(组合计数)

下面的图形是著名的杨辉三角形&#xff1a; 如果我们按从上到下、从左到右的顺序把所有数排成一列&#xff0c;可以得到如下数列&#xff1a; 1, 1, 1, 1, 2, 1, 1, 3, 3, 1, 1, 4, 6, 4, 1, ... 给定一个正整数 N&#xff0c;请你输出数列中第一次出现 N是在第几个数&#x…

LeetCode-74. 搜索二维矩阵【数组 二分查找 矩阵】

LeetCode-74. 搜索二维矩阵【数组 二分查找 矩阵】 题目描述&#xff1a;解题思路一&#xff1a;先二分查找行&#xff0c;再二分查找列。解题思路二&#xff1a;暴力遍历&#xff0c;也能过。解题思路三&#xff1a;用python的in。 题目描述&#xff1a; 给你一个满足下述两条…

HAL STM32 定时器PWM DMA输出方式

HAL STM32 定时器PWM DMA输出方式 &#x1f9e8;遗留问题&#xff1a;当配置RCR重复计数器&#xff0c;配置为2时&#xff0c;在定义了3组PWM参数情况下&#xff0c;只能输出第二组参数的PWM波形。&#xff08;HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1, aCCValue_Buff…

实时计算平台设计方案:913-基于100G光口的DSP+FPGA实时计算平台

基于100G光口的DSPFPGA实时计算平台 一、产品概述 基于以太网接口的实时数据智能计算一直应用于互联网、网络安全、大数据交换的场景。以DSPFPGA的方案&#xff0c;体现了基于硬件计算的独特性能&#xff0c;区别于X86GPU的计算方案&#xff0c;保留了高带宽特性&…

Python学习笔记——heapq

堆排序 思路 堆排序思路是&#xff1a; 将数组以二叉树的形式分析&#xff0c;令根节点索引值为0&#xff0c;索引值为index的节点&#xff0c;子节点索引值分别为index*21、index*22&#xff1b;对二叉树进行维护&#xff0c;使得每个非叶子节点的值&#xff0c;都大于或者…

Android匿名共享内存(Ashmem)

在Android中我们熟知的IPC方式有Socket、文件、ContentProvider、Binder、共享内存。其中共享内存的效率最高&#xff0c;可以做到0拷贝&#xff0c;在跨进程进行大数据传输&#xff0c;日志收集等场景下非常有用。共享内存是Linux自带的一种IPC机制&#xff0c;Android直接使用…

Autodesk AutoCAD 2025 (macOS, Windows) - 自动计算机辅助设计软件

Autodesk AutoCAD 2025 (macOS, Windows) - 自动计算机辅助设计软件 AutoCAD 2024 开始原生支持 Apple Silicon&#xff0c;性能提升至 2 倍 请访问原文链接&#xff1a;https://sysin.org/blog/autodesk-autocad/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处…

Elastic AI Assistant for Observability 和 Microsoft Azure OpenAI 入门

作者&#xff1a;来自 Elastic Jonathan Simon 最近&#xff0c;Elastic 宣布 AI 观测助手现已正式向所有 Elastic 用户开放。该 AI 观测助手为 Elastic 观测提供了一种新工具&#xff0c;提供了大型语言模型&#xff08;LLM&#xff09;连接的聊天和上下文洞察&#xff0c;以解…

Windows11配置VUE开发环境

目录 一、按照nodejs二、命令安装npm cache clean --forcenpm install -g vue/clinpm install npm -gnpm install webpacknpm install vue-cli -g与npm install -g vue/cli区别npm install -g cnpm --registryhttps://registry.npm.taobao.orgnpm i yarn -g --verbosenpm i -g …