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…

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

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

电脑显示不出网络

你的电脑是否在开机后显示不出网络&#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

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…

网工内推 | 国企信息安全工程师,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;具备完善的配…

ai写作助手有哪些,5款强大的ai写作工具为你所用

在科技日新月异的时代&#xff0c;人工智能已经悄然走进我们的生活&#xff0c;为我们带来了诸多便利。其中&#xff0c;AI写作助手作为一种创新的工具&#xff0c;正在改变着我们的写作方式。它们不仅能够提供创意灵感&#xff0c;还能帮助我们提高写作效率&#xff0c;让文字…

【RAG论文】文档树:如何提升长上下文、非连续文档、跨文档主题时的检索效果

RAPTOR Recursive Abstractive Processing for Tree-Organized RetrievalICLR 2024 Stanfordhttps://arxiv.org/pdf/2401.18059 RAPTOR&#xff08;Recursive Abstractive Processing for Tree-Organized Retrieval&#xff09;是一种创建新的检索增强型语言模型&#xff0c;它…

《Intel开发手册卷3》读书笔记3

1、中断和异常的概述 中断和异常是强制性的执行流的转移&#xff0c;从当前正在执行的程序或任务转移到一个特殊的称作句柄的例程或任务。当硬件发出信号时&#xff0c;便产生中断&#xff0c;中断的产生同正在执行的程序是异步的&#xff0c;即中断的产生是随机的。其用于处理…

JVM(四)

在上一篇中&#xff0c;介绍了JVM组件中的运行时数据区域&#xff0c;这一篇主要介绍垃圾回收器 JVM架构图&#xff1a; 1、垃圾回收概述 在第一篇中介绍JVM特点时&#xff0c;有提到过内存管理&#xff0c;即Java语言相对于C&#xff0c;C进行的优化&#xff0c;可以在适当的…

【Postman接口测试】第四节.Postman接口测试项目实战(上)

文章目录 前言一、项目介绍 1.1 项目界面功能介绍 1.2 项目测试接口介绍 1.3 项目测试接口流程二、HTTP协议三、接口测试中接口规范四、项目合同新增业务介绍 4.1 登录接口调试 4.1 登录接口自动关联 4.1 添加课程接口调试 4.1 上传合同…