当前位置: 首页 > news >正文

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

相关文章:

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

目录 一&#xff1a;介绍二&#xff1a;list的创建和方法创建list方法 三&#xff1a;list的具体用法3.1 push_back、pop_back、push_front、pop_front3.2 insert() 和 erase()3.3 splice 函数 四&#xff1a;list容器底层实现4.1 list 容器节点结构5.2 list容器迭代器的底层实…...

IEEE Latex模版踩雷避坑指南

参考文献 原Latex模版 \begin{thebibliography}{1} \bibliographystyle{IEEEtran}\bibitem{ref1} {\it{Mathematics Into Type}}. American Mathematical Society. [Online]. Available: https://www.ams.org/arc/styleguide/mit-2.pdf\bibitem{ref2} T. W. Chaundy, P. R. Ba…...

【C++】类与对象——多态详解

目录 一、多态的定义 二、重载、覆盖(重写)、隐藏(重定义)的对比 三、析构函数重写 四、C11 override 和 final 1. final 2. override 五、抽象类 六、多态的原理 一、多态的定义 多态是在不同继承关系的类对象&#xff0c;去调用同一函数&#xff0c;产生了不同的行为…...

WordPress建网站公司 建易WordPress建站

建易WordPress建网站公司是一家专业从事WordPress网站建设、网站维护、网站托管、运营推广和搜索引擎优化(SEO)等服务的公司。建易WordPress建网站公司提供多种服务&#xff0c;包括模板建站和定制网站&#xff0c;并且明码标价&#xff0c;价格透明&#xff0c;竭诚为全国各地…...

MySQL正则替换整个单词

\b 是正则表达式规定的一个特殊代码&#xff08;好吧&#xff0c;某些人叫它元字符&#xff0c;metacharacter&#xff09;&#xff0c;代表着单词的开头或结尾&#xff0c;也就是单词的分界处。虽然通常英文的单词是由空格&#xff0c;标点符号或者换行来分隔的&#xff0c;但…...

Java设计模式:享元模式实现高效对象共享与内存优化(十一)

码到三十五 &#xff1a; 个人主页 目录 一、引言二、享元设计模式的概念1. 对象状态的划分2. 共享机制 三、享元设计模式的组成四、享元设计模式的工作原理五、享元模式的使用六、享元设计模式的优点和适用场景结语 [参见]&#xff1a; Java设计模式&#xff1a;核心概述&…...

景源畅信电商:抖音开店步骤是什么?

随着社交媒体的兴起&#xff0c;抖音已经成为一个不可忽视的电商平台。许多人都希望通过抖音开店来实现自己的创业梦想。那么&#xff0c;抖音开店的具体步骤是什么呢?接下来&#xff0c;我们将详细阐述这一问题。 一、明确回答问题抖音开店的步骤主要包括&#xff1a;注册账号…...

Notepad++不显示CRLF的方法

View -> Show Symbol -> 去掉勾选 Show All Characters...

前端开发工程师——AngularJS

一.表达式和语句 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-w…...

【AI算法岗面试八股面经【超全整理】——概率论】

AI算法岗面试八股面经【超全整理】 概率论信息论机器学习CVNLP 目录 1、古典概型、几何概型2、条件概率、全概率公式、贝叶斯公式3、先验概率、后验概率4、离散型随机变量的常见分布5、连续型随机变量的常见分别6、数学期望、方差7、协方差、相关系数8、独立、互斥、不相关9.大…...

vue3 使用vant

使用前提&#xff1a; vite创建的vue3项目 vanthttps://vant-ui.github.io/vant/#/zh-CN/home npm i vant 引入样式&#xff1a; main.js import vant/lib/index.css vant封装 import { showLoadingToast,closeToast,showDialog,showConfirmDialog } from vant;export func…...

网络请求客户端WebClient的使用

在 Spring 5 之前&#xff0c;如果我们想要调用其他系统提供的 HTTP 服务&#xff0c;通常可以使用 Spring 提供的 RestTemplate 来访问&#xff0c;不过由于 RestTemplate 是 Spring 3 中引入的同步阻塞式 HTTP 客户端&#xff0c;因此存在一定性能瓶颈。根据 Spring 官方文档…...

unity制作app(9)--拍照 相册 上传照片

1.传输照片&#xff08;任何较大的数据&#xff09;都需要扩展服务器的内存空间。 2.还需要base64编码 2.1客户端发送位置的编码 2.2服务器接收部分的代码...

【busybox记录】【shell指令】mkfifo

目录 内容来源&#xff1a; 【GUN】【mkfifo】指令介绍 【busybox】【mkfifo】指令介绍 【linux】【mkfifo】指令介绍 使用示例&#xff1a; 创建管道文件 - 创建的时候同时指定文件权限 常用组合指令&#xff1a; 指令不常用/组合用法还需继续挖掘&#xff1a; 内容来…...

使用Jmeter进行性能测试的基本操作方法

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…...

Linux学习笔记(epoll,IO多路复用)

Linux learning note 1、epoll的使用场景2、epoll的使用方法和内部原理2.1、创建epoll2.2、使用epoll监听和处理事件 3、示例 1、epoll的使用场景 epoll的英文全称是extend poll&#xff0c;顾名思义是poll的升级版。常见的IO复用技术有select&#xff0c;poll&#xff0c;epo…...

STM32定时器及输出PWM完成呼吸灯

文章目录 一、STM32定时器原理1、基本定时器2、通用定时器&#xff08;1&#xff09;时钟源&#xff08;2&#xff09;预分频器PSC&#xff08;3&#xff09;计数器CNT&#xff08;4&#xff09;自动装载寄存器ARR 3、高级定时器 二、PWM工作原理三、控制LED以2s的频率周期性地…...

海外仓管理系统费用解析:如何选择高性价比的海外仓系统

海外仓作为链接国内商家和海外市场的重要环节&#xff0c;其重要性自然是不言而喻的。 对于众多中小型海外仓来说&#xff0c;如何在保证服务质量的同时降低运营成本&#xff0c;就成了大家关注的焦点。今天我们就从海外仓管理系统的费用这个角度&#xff0c;来帮助大家分析一…...

深度学习之学习率调度器Scheduler介绍

调度器是深度学习训练过程中非常重要的一部分,它用于动态调整模型的学习率,从而提高训练效率和最终性能。 1. 为什么需要学习率调度器? 深度学习训练中,学习率是一个非常关键的超参数。合适的学习率可以确保模型快速收敛并获得良好的性能。 但是在训练过程中,最优的学习率会随…...

蓝桥杯-AB路线(详细原创)

问题描述&#xff1a; 有一个由 N M 个方格组成的迷宫&#xff0c;每个方格写有一个字母 A 或者 B。小蓝站在迷宫左上角的方格&#xff0c;目标是走到右下角的方格。他每一步可以移动到上下左右相邻的方格去。 由于特殊的原因&#xff0c;小蓝的路线必须先走 K 个 A 格子、再…...

计算机字符编码的发展

目录 背景 发展 第一阶段&#xff1a;ASCII编码 第二阶段&#xff1a;扩展ASCII编码 第三阶段&#xff1a;各国编码 第四阶段&#xff1a;Unicode编码 第五阶段&#xff1a;UTF系列编码方式 相关扩展 背景 在计算机诞生初期&#xff0c;所有的数据都是基于二进制数&am…...

Java(六)——抽象类与接口

文章目录 抽象类和接口抽象类抽象类的概念抽象类的语法抽象类的特性抽象类的意义 接口接口的概念接口的语法接口的特性接口的使用实现多个接口接口与多态接口间的继承抽象类和接口的区别 抽象类和接口 抽象类 抽象类的概念 Java使用类实例化对象来描述现实生活中的实体&…...

【4.vi编辑器使用(下)】

一、vi编辑器的光标移动 二、vi编辑器查找命令 1、命令&#xff1a;:/string 查找字符串 n&#xff1a;继续查找 N&#xff1a;反向继续查找 /^the 查找以the开头的行 /end 查找以 查找以 查找以结尾的行 三、vi编辑器替换命令 1、语法: : s[范围,范围]str1/str2[g] g表示全…...

【数据结构】探索树中的奇妙世界

专栏介绍&#xff1a; 哈喽大家好&#xff0c;我是野生的编程萌新&#xff0c;首先感谢大家的观看。数据结构的学习者大多有这样的想法&#xff1a;数据结构很重要&#xff0c;一定要学好&#xff0c;但数据结构比较抽象&#xff0c;有些算法理解起来很困难&#xff0c;学的很累…...

搭建YOLOv10环境 训练+推理+模型评估

文章目录 前言一、环境搭建必要环境1. 创建yolov10虚拟环境2. 下载pytorch (pytorch版本>1.8)3. 下载YOLOv10源码4. 安装所需要的依赖包 二、推理测试1. 将如下代码复制到ultralytics文件夹同级目录下并运行 即可得到推理结果2. 关键参数 三、训练及评估1. 数据结构介绍2. 配…...

c++(一)

c&#xff08;一&#xff09; C与C有什么区别命名空间使用 输入输出流引用指针和引用的区别定义拓展 函数重载例子测试函数重载原理 参数默认值什么是参数默认值注意 在c中如何引入c的库动态内存分配new、delete与malloc、free的区别&#xff1f; C与C有什么区别 <1>都是…...

java面试中高频问题----1

一、乐观锁和悲观锁定义、场景怎么判断用什么&#xff1f; 1.乐观锁&#xff1a; 定义&#xff1a;乐观锁假设大多数情况下&#xff0c;资源不会发生冲突。因此&#xff0c;允许多个线程同时访问资源。 场景&#xff1a;读操作多&#xff0c;写操作少&#xff0c;数据冲突概率…...

ABB 控制柜

1&#xff0c;主计算机&#xff1a;相当于电脑的主机&#xff0c;用于存放系统和数据&#xff0c;需要24V直流电才能工作。执行用户编写的程序&#xff0c;控制机器人进行响应的动作。主计算机有很多接口&#xff0c;比如与编程PC连接的服务网口、用于连接示教器的网口、连接轴…...

【错误记录】HarmonyOS 运行报错 ( Failure INSTALL_PARSE_FAILED_USESDK_ERROR )

文章目录 一、报错信息二、问题分析三、解决方案 一、报错信息 在 DevEco Studio 中 , 使用 远程设备 , 向 P40 Failure[INSTALL_PARSE_FAILED_USESDK_ERROR] compileSdkVersion and releaseType of the app do not match the apiVersion and releaseType on the device. 二、…...

使用C语言openssl库实现 RSA加密 和 消息验证

Q&#xff1a;什么是RSA&#xff1f; A&#xff1a;RSA&#xff08;Rivest-Shamir-Adleman&#xff09;是一种非对称加密算法&#xff0c;是最早的一种用于公开密钥加密和数字签名的算法。它使用一对公钥&#xff08;public key&#xff09;和私钥&#xff08;private key&…...