C++STL容器系列(三)list的详细用法和底层实现

目录

  • 一:介绍
  • 二:list的创建和方法
    • 创建list
    • 方法
  • 三:list的具体用法
    • 3.1 push_back、pop_back、push_front、pop_front
    • 3.2 insert() 和 erase()
    • 3.3 splice 函数
  • 四:list容器底层实现
    • 4.1 list 容器节点结构
    • 5.2 list容器迭代器的底层实现
    • 5.2 list容器的主体实现
  • 总结

对2024年航空航天与力学国际学术会议(ICAM 2024) 感兴趣的伙伴可以点击【click】

一:介绍

  • list是一种序列式容器。该容器的底层是用双向链表实现的, 在链表的任一位置进行元素的插入、删除操作都是快速的。

二:list的创建和方法

首先,使用vector时需包含头文件:

  • #include <list>

创建list

list本质是类模板,可以存储任何类型的数据。数组在声明前需要加上数据类型,而list则通过模板参量设定类型。

比如,声明一个int型的list列表。

list<A> listname;					// 一个空列表list<A> listname(size);				// 开辟size个空间,值默认为0list<A> listname(size, value);      // size个值为value的数组list<A> listname(elselist);         //  拷贝构造参数是其他listlist<A> listname(first, last);      // 迭代器初始化

方法

✨iterators(迭代器) \colorbox{pink}{✨iterators(迭代器)} ✨iterators(迭代器)

名字描述
begin返回指向容器中第一个元素的迭代器。
end返回指向容器最后一个元素所在位置后一个位置的迭代器
rbegin返回容器逆序的第一个元素的迭代器
rend返回容器逆序的最后一个元素的前一个位置的迭代器
cbegin和begin()功能相同,在其基础上增加了 const 属性,不能用于修改元素。
cend和end()功能相同,在其基础上增加了 const 属性,不能用于修改元素。
crbegin和rbegin()功能相同,在其基础上增加了 const 属性,不能用于修改元素。
crend和rend()功能相同,在其基础上增加了 const 属性,不能用于修改元素。

✨Capacity(容量) \colorbox{pink}{✨Capacity(容量)} ✨Capacity(容量)

名字描述
size返回实际元素的个数
empty判断list是否为空,为空返回true否则false
max_size返回元素个数的最大值。

✨Element access(元素访问) \colorbox{pink}{✨Element access(元素访问)} ✨Element access(元素访问)

名字描述
front返回第一个元素
back返回最后一个元素

✨Modifiers(修改器) \colorbox{pink}{✨Modifiers(修改器)} ✨Modifiers(修改器)

名字描述
push_back在容器的尾部插入元素
pop_back删除最后一个元素
push_front在容器的头部插入元素
pop_front删除第一个元素
erase删除元素
clear清除容器内容,size=0,存储空间不变
swap交换两个元素的所有内容
assign用新元素替换原有内容。
emplace_front插入元素,和insert实现原理不同,速度更快
emplace_back在容器的尾部插入元素,和push_back不同, 速度更快

三:list的具体用法

3.1 push_back、pop_back、push_front、pop_front

list<int> lt1;
for (int i = 0; i < 5; i++)
{lt1.push_back(i);lt1.push_front(i);
}
for (int i = 0; i < 5; i++)
{lt1.pop_back();lt1.pop_front();
}
  • emplace_back的效果和push_back一样,具体可以查看前一篇vector的博客

3.2 insert() 和 erase()

  • insert():在指定位置插入一个或多个元素(三个重载)
l1.insert(l1.begin(), 100);  在l1的开始位置插入100。l1.insert(l1.begin(), 2, 200);  在l1的开始位置插入2100。l1.insert(l1.begin(), l2.begin(), l2.end()); 在l1的开始位置插入l2的从开始到结束的所有位置的元素。
  • erase():删除一个元素或一个区域的元素(两个重载)
l1.erase(l1.begin());  	 将l1的第一个元素删除。l1.erase(l1.begin(), l1.end());  将l1的从 begin()end() 之间的元素删除。

3.3 splice 函数

  • splice函数是list中的一个转移函数,将另外一个list中的元素转移到本list中。共有3个重载:

1、list1.splice(position, list2): 将list2中的所有元素剪贴到list1的position位置;

2、list1.splice(position, list2, iter): 将list2中某个位置的迭代器iter指向的元素剪贴到list1中的position位置;

3、list1.splice(position, list2, iter1, iter2): 将list2中的某一段位置iter1 ~ iter2的元素剪贴到list1中的position位置

void list_splice()
{list<int>mylist1, mylist2;for (int i = 1; i <= 4; i++) mylist1.push_back(i);for (int i = 1; i <= 4; i++) mylist2.push_back(i * 10);std::list<int>::iterator it1 = mylist1.begin();it1++;mylist1.splice(it1, mylist2);  // 转移mylist2到it1位置之前// 注意:此时链表2就为空链表了 所以splice名字叫做转移for (auto& e : mylist1){cout << e << " ";}
}// 输出:1 10 20 30 40 2 3 4

四:list容器底层实现

容器的底层是用双向链表实现的,甚至一些 STL 版本中(比如 SGI STL),list 容器的底层实现使用的是双向循环链表。

头指针,使用链表存储数据,并不会将它们存储到一整块连续的内存空间中。恰恰相反,各元素占用的存储空间(又称为节点)是独立的、分散的,它们之间的线性关系通过指针来维持。

在这里插入图片描述

4.1 list 容器节点结构

双向链表的各个节点中存储的不仅仅是元素的值,还应包含 2 个指针,分别指向前一个元素和后一个元素。

template<class T>  
struct ListNode    // 当一个类不想要访问限定符限制的时候就用struct
{ListNode* _next;ListNode* _prev;T _data;ListNode(const T& data):_next(nullptr), _prev(nullptr), _data(data){}
};

注:为了方便阅读和理解,本文章所实现的list都省略了与本文核心内容不相关的内容,如果读者对此部分感兴趣,可查看 list 容器实现源码。

  • 本人的 gitee上具有本文实现的完整代码及测试代码,需要的可以自取【click】

5.2 list容器迭代器的底层实现

和 vector 这些容器迭代器的实现方式不同,由于 list 容器的元素并不是连续存储的,所以该容器迭代器中,必须包含一个可以指向 list 容器的指针,并且该指针还可以借助重载的 *、++、–、==、!= 等运算符,实现迭代器正确的递增、递减、取值等操作。

  • 所以我们就需要对迭代器进行封装!!

可以看到,迭代器的移动就是通过操作节点的指针实现的。

  • 注意:因为存在const迭代器 和 非const迭代器 所以这里我们的模版参数有三个一个为存储类型 一个为 引用返回类型 一个为 指针类型(const 和 非const)
// 通过模板,给不同的模板参数,让编译器帮我们实例化两个类
template<class T, class Ref, class Ptr>
struct ListIterator
{typedef ListNode<T> Node;typedef ListIterator<T, Ref, Ptr> Self;Node* _node;ListIterator(Node* node):_node(node){}Self& operator++()  // 迭代器++返回自己 所以typedef一下{_node = _node->_next;return *this;}Self operator++(int)   // 后置++ 不能用引用返回了 tmp会销毁{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;}Ptr operator->(){return &_node->_data;}Ref operator*(){return _node->_data;}bool operator!=(const Self& it){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}
};
  • 接下来是reverse_iterator的实现 注意反向迭代器的实现可以对正向迭代器进行复用 具体代码如下:
// 此处的参数就是正向迭代器了
template<class iterator, class Ref, class Ptr>
struct Reverse_ListIterator
{typedef Reverse_ListIterator<iterator, Ref, Ptr> Self;iterator _it;Reverse_ListIterator(iterator it):_it(it){}Ref operator*(){iterator tmp = _it;return tmp._node->_prev;}// 作用是为pos这种结构体服务的Ptr operator->(){return &_it._node->_prev->_data;}Self& operator++(){_it._node = _it._node->_prev;return *this;}Self operator++(int){Self tmp(*this);_it._node = _it._node->_prev;return tmp;}Self& operator--(){_it._node = _it._node->_next;return *this;}Self operator--(int){Self tmp(*this);_it._node = _it._node->_next;return tmp;}bool operator!=(const Self& it){return _it._node != it._it._node;    // 复用的结果}bool operator==(const Self& it){return _it._node == it._node;}};

5.2 list容器的主体实现

  • 以下是主体实现
template<class T>
class list
{typedef ListNode<T> Node;// 不符合迭代器的行为,无法遍历 无法进行++和*操作 所以我们要将迭代器封装成一个类进行运算符重载即可// typedef Node* iterator;
public:typedef ListIterator<T, T&, T*> iterator;   //规范迭代器typedef ListIterator<T, const T&, const T*> const_iterator;   // 通过模板,给不同的模板参数,让编译器帮我们实例化两个类// list的const迭代器是有东西的typedef Reverse_ListIterator<iterator, T&, T*> reverse_iterator;typedef Reverse_ListIterator<const_iterator, const T&, const T*> const_reverse_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);}reverse_iterator rbegin(){return reverse_iterator(end());    // 匿名对象}reverse_iterator rend(){return reverse_iterator(begin());}list()   // 初始化哨兵卫的头节点{_head = new Node(T());   // 没有合适的默认构造函数可用 这里用匿名对象初始化_head->_next = _head;_head->_prev = _head;}void empty_init(){_head = new Node(T());_head->_next = _head;_head->_prev = _head;}list(const list<T>& lt){empty_init();for (const auto& e : lt){push_back(e);}}// 这里的赋值重载是现代写法list<T>& operator=(list<T> lt){swap(_head, lt._head);return *this;}~list(){clear();delete _head;}void push_back(const T& x){Node* newnode = new Node(x);    // 就不需要专门写个CreatNewNode的了   自动调用Node的构造函数/*newnode->_next = _head;newnode->_prev = _head->_prev;_head->_prev->_next = newnode;_head->_prev = newnode;*/insert(end(), x);}iterator insert(iterator pos, const T& x){Node* node = new Node(x);Node* cur = pos._node;Node* prev = cur->_prev;node->_next = cur;node->_prev = prev;prev->_next = node;cur->_prev = node;return iterator(node);}iterator erase(iterator pos){assert(pos != end());Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;return iterator(next);}void pop_back(){erase(--end());}void push_front(const T& x){insert(begin(), x);}void pop_front(){erase(begin());}void clear(){list<T>::iterator it = begin();while (it != end()){it = erase(it);}}private:Node* _head;
};

总结

list的详细用法和底层实现就介绍到这里了如果有任何疑问都可以私信我,希望我们共同进步, 有错误还请在评论区指正!

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

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

相关文章

【window 安装 service bus explorer】

安装ServiceBusExplorer 首先需要安装Chocolatey安装 service bus explorer 首先需要安装Chocolatey 参考&#xff1a; https://chocolatey.org/install#install-step2 以管理员身份运行powershell输入Get-ExecutionPolicy回车&#xff0c;若显示 Restricted输入Set-Executio…

CentOS8环境下FTP服务器安装与配置

在本指南中&#xff0c;我们将一步步介绍如何在CentOS 8环境下安装和配置一个FTP服务器。FTP&#xff08;文件传输协议&#xff09;是一种网络传输协议&#xff0c;用于在网络中的计算机之间传输文件。虽然现在有更安全的传输方式&#xff0c;如SFTP或FTP over SSL&#xff0c;…

python -【一】基础语法

python 基础语法 一. 基础数据类型 常用的 6 种数据类型 类型描述说明数字&#xff08;Number&#xff09;int&#xff0c;float&#xff0c;complex(复数)&#xff0c;bool复数&#xff1a;4 3j&#xff0c;j 表示复数字符串&#xff08;String&#xff09;文本&#xff1…

小短片创作-理论知识(三)

1、抗锯齿 1.相机移动的时候出现锯齿 2.当1个像素在三角形边缘的时候&#xff0c;可能取值为白色&#xff0c;也可能取值为黑色&#xff0c;表现出来就是闪烁&#xff0c;或锯齿 3.如果我们通过超采样将1个像素变成4个像素进行计算&#xff0c;得到的结果就会更准确&#x…

Codeforces Round 945 (Div. 2) C. Cat, Fox and Double Maximum 题解 贪心 构造

Cat, Fox and Double Maximum 题目描述 Fox loves permutations! She came up with the following problem and asked Cat to solve it: You are given an even positive integer n n n and a permutation † ^\dagger † p p p of length n n n. The score of another …

电脑显示不出网络

你的电脑是否在开机后显示不出网络&#xff0c;或者有网络消失的现象&#xff1f;今天和大家分享我学到的一个办法&#xff0c;希望对大家有用。 分析出现这类现象的原因&#xff1a;可能是电脑网卡松动了&#xff0c;电脑中存在静电流。 解决办法&#xff1a;先将电脑关机&am…

深度学习(一)

深度学习&#xff08;一&#xff09; 一、实验目的 掌握前馈全连接神经网络&#xff0c;具体包括&#xff1a; (1) 前馈全连接神经网络的网络结构 (2) 前馈神全连接经网络的工作原理 (3) 前馈全连接神经网络的代码实现 二、实验内容 1. 导入常用工具包 2. 数据导入与数据…

大模型对齐方法笔记二:基于Rank的对齐方法RRHF和PRO

文章目录 RRHFPRO将RLHF嫁接到PRO 参考资料 RRHF RRHF(Rank Responses to align Human Feedback)出自2023年4月的论文《RRHF: Rank Responses to Align Language Models with Human Feedback without tears》&#xff0c;是较早提出的不需要使用PPO来对齐人类偏好的方法。 设…

nginx安装部署问题

记一次nginx启动报错问题处理 问题1 内网部署nginx&#xff0c;开始执行make&#xff0c;执行不了&#xff0c;后面装了依赖的环境 yum install gcc-c 和 yum install -y pcre pcre-devel 问题2&#xff0c;启动nginx报错 解决nginx: [emerg] unknown directive “stream“ in…

Keil5 ~STM32报错Solutions#1

一、error: #268: declaration may not appear after executable statement in block

tar 详细说明

tar命令在Unix和类Unix系统中被广泛用于打包和压缩文件。以下是对tar命令的详细说明&#xff1a; 一、命令概述 tar命令的名称来源于“tape archive”&#xff08;磁带存档&#xff09;&#xff0c;最初设计用于在磁带上创建备份。现在&#xff0c;它已成为在Unix和类Unix系统…

Thingsboard规则链:Customer Details节点详解

在物联网&#xff08;IoT&#xff09;平台Thingsboard的规则引擎体系中&#xff0c;Customer Details节点是一个功能强大的组件&#xff0c;它专为处理与客户&#xff08;Customer&#xff09;实体相关的综合信息而设计。这个节点不仅能够读取客户的基本属性&#xff0c;还能提…

【NumPy】全面解析subtract函数:高效数组减法指南

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

GPT-4o:人工智能新纪元的开端

引言 近年来&#xff0c;人工智能领域的发展日新月异&#xff0c;特别是在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;各种生成预训练模型不断推陈出新。自OpenAI发布GPT-3以来&#xff0c;生成预训练模型在文本生成、语言理解等任务中展现了强大的能力。近期&a…

报表中的某个单元格如何绑定实时数据库的统计值?

在报表中绑定实时数据库的统计值通常涉及几个关键步骤。具体方法可能因所使用的报表工具、数据库类型和技术栈而异&#xff0c;但以下是一个一般性的流程&#xff1a; 1. 确定数据源&#xff1a; 首先&#xff0c;你需要明确你的报表要连接哪个数据库或数据源。这可能是关系型…

网工内推 | 国企信息安全工程师,CISP认证优先

01 浙江省公众信息产业有限公司 &#x1f537;招聘岗位&#xff1a;安全运营工程师 &#x1f537;职责描述&#xff1a; 1. 负责公司内部安全运营平台及其子系统的安全事件管理、事件发现分析、应急响应和系统维护等&#xff1b; 2. 负责风险和漏洞管理&#xff0c;包括漏洞预…

一行命令将已克隆的本地Git仓库推送到内网服务器

一、需求背景 我们公司用gitea搭建了一个git服务器&#xff0c;其中支持win7的最高版本是v1.20.6。 我们公司的电脑在任何时候都不能连接外网&#xff0c;但是希望将一些开源的仓库移植到内网的服务器来。一是有相关代码使用的需求&#xff0c;二是可以建设一个内网能够查阅的…

2019美亚

1.何源是一名 25 岁的客服人员&#xff0c;在一间电讯公司工作。某日&#xff0c;何源在用 iPhone 手机在政府建筑物 中偷拍车牌期间被警员截停&#xff0c;盘问期间警员检查手机相册发现多张车牌图片&#xff0c;何源情绪紧张&#xff0c;趁 警员不被&#xff0c;抢过手机丢入…

模型实战(22)之 C++ - tensorRT部署yolov8-cls 目标分类

C++ - tensorRT部署yolov8-cls 目标分类 在检测应用场景中如果有同等类别不同形态的目标,单纯的目标检测可能达不到实用或者想要的精度,这就需要衔接一步分类python环境下如何直接调用推理模型转换并导出:pt -> onnx ->.engineC++ tensorrt 部署分类模型1.Python环境下…

OrangePi Kunpeng Pro 开发板测评 | AI 边缘计算 大模型部署

0 前言 此次很幸运能够参与 OrangePi Kunpeng Pro 开发板的测评&#xff0c;感谢 CSDN 给予这次机会。 香橙派联合华为发布了基于昇腾的 OrangePi Kunpeng Pro 开发板&#xff0c;具备 8TOPS 的 AI 算力&#xff0c;能覆盖生态开发板者的主流应用场景&#xff0c;具备完善的配…