【C++】 初阶篇 STL list的使用

前言

这篇笔记是我的实践记录:从节点、迭代器到容器接口,一步步还原list的核心逻辑,把 “用容器” 变成 “懂容器”。

一、list的介绍

list是 STL 中支持在任意位置高效(常数时间)插入、删除的双向迭代序列式容器,底层基于带头双向链表实现 —— 每个元素存于独立节点,通过指针连接前后元素;它和forward_list类似但后者是单链表、仅支持前向迭代;和array、vector、deque相比,list的插入删除效率更优,但缺点是不支持随机访问(访问第 n 个元素需线性遍历),且每个节点的指针会占用额外空间(对存储小元素的大list影响较明显)。

二、list的常用接口及其使用

2.1 构造函数

构造函数 (constructor)接口说明
list (size_type n, const value_type& val = value_type())构造的 list 中包含 n 个值为 val 的元素
list()构造空的 list
list (const list& x)拷贝构造函数
list (InputIterator first, InputIterator last)用迭代器区间构造 list

2.2 增删查改

函数声明接口说明
push_front在 list 首元素前插入值为 val 的元素
pop_front删除 list 中第一个元素
push_back在 list 尾部插入值为 val 的元素
pop_back删除 list 中最后一个元素
insert在 list position 位置中插入值为 val 的元素
erase删除 list position 位置的元素
swap交换两个 list 中的元素
clear清空 list 中的有效元素
//尾插l1.push_back(0);l1.push_back(1);l1.push_back(2);for(autov:l1){cout<<v<<" ";}cout<<endl;//头插l1.push_front(-1);for(autov:l1){cout<<v<<" ";}cout<<endl;//头删l1.pop_front();for(autov:l1){cout<<v<<" ";}cout<<endl;//在pos处插入//先来一个迭代器后面我们具体讲list<int>::iterator it1=l1.begin();//++it就可以在开始的下一个位置插入l1.insert(++it1,999);for(autov:l1){cout<<v<<" ";}cout<<endl;l1.erase(--it1);for(autov:l1){cout<<v<<" ";}cout<<endl;for(autov:l2){cout<<v;}cout<<endl;l1.swap(l2);for(autov:l1){cout<<v<<" ";}cout<<endl;for(autov:l2){cout<<v<<" ";}cout<<endl;l1.clear();for(autov:l1){cout<<v<<" ";}cout<<endl;

  • swap接口,为了避免因使用std::swap交换效率低下(需要拷贝),list重载了swap,这个重载的底层是交换两个链表的指针。
  • list没有重载find,需要调用std::find。

2.3list特殊接口

除了上述 STL 容器基本都有的一般接口外,list还提供一些独有的特殊操作接口,如下:

函数声明接口说明
splice将 list1 中的元素转移到 list2 中
remove移除 list 中的指定元素
unique链表去重
merge合并两个链表
sort链表排序
reverse链表逆置

2.3.1 splice 接口

有两种使用方式:

  1. 从一个list剪切到另外一个list的一个位置前面
cout<<"l1:";for(autov:l1){cout<<v;}cout<<endl;cout<<"l2:";for(autov:l2){cout<<v;}cout<<endl;l1.splice(l1.begin(),l2);cout<<"l1:";for(autov:l1){cout<<v;}cout<<endl;cout<<"l2:";for(autov:l2){cout<<v;}cout<<endl;

  1. 从自身的某个位置开始剪切到自身的某个位置前面。
// splice( 插入位置, 源链表, 源区间起点, 源区间终点 )lt.splice(pos,source,first,last);

2.3.2 remove 接口

通过元素的值来移除元素。

for(autov:l2){cout<<v;}cout<<endl;l2.remove(6);for(autov:l2){cout<<v;}cout<<endl;

2.3.3 unique 接口

去重要求链表有序,原因是底层是默认相同的数据被放在一起。

for(autov:l2){cout<<v;}cout<<endl;l2.unique();for(autov:l2){cout<<v;}cout<<endl;

2.3.4 merge 接口

合并链表,需要两个链表有序,底层有点类似于归并排序的逻辑,双指针遍历两个链表找小插入再返回拷贝。被合并的list会变空。

list<int>l1;list<int>l2;l1.push_back(1);l1.push_back(3);l1.push_back(5);l1.push_back(7);l2.push_back(0);l2.push_back(2);l2.push_back(4);l2.push_back(6);l2.push_back(8);cout<<"l1:";for(autov:l1){cout<<" "<<v;}cout<<endl;cout<<"l2:";for(autov:l2){cout<<" "<<v;}cout<<endl;l1.merge(l2);cout<<"l1:";for(autov:l1){cout<<" "<<v;}cout<<endl;cout<<"l2:";for(autov:l2){cout<<" "<<v;}cout<<endl;

2.3.5 sort 接口

链表排序只能使用list提供的sort接口,而不能使用 algorithm 提供的sort接口,因为链表物理地址不连续,迭代器为双向迭代器,不支持 + - 操作,而算法库中的sort函数需要支持 + - 的随机迭代器;

list<int>l1;list<int>l2;l1.push_back(1);l1.push_back(3);l1.push_back(5);l1.push_back(7);l1.push_back(0);l1.push_back(2);l1.push_back(4);l1.push_back(6);l1.push_back(8);cout<<"l1:";for(autov:l1){cout<<" "<<v;}cout<<endl;l1.sort();cout<<"l1:";for(autov:l1){cout<<" "<<v;}cout<<endl;

2.3.6 reverse

cout<<"l1:";for(autov:l1){cout<<" "<<v;}cout<<endl;l1.reverse();cout<<"l1:";for(autov:l1){cout<<" "<<v;}cout<<endl;

2.3.7 总结

1、链表排序只能使用list提供的sort接口,而不能使用 algorithm 提供的sort接口,因为链表物理地址不连续,迭代器为双向迭代器,不支持 + - 操作,而算法库中的sort函数需要支持 + - 的随机迭代器;
2、链表去重之前必须保证链表有序,否则去重不完全;
3、两个有序链表合并之后仍然保存有序;

最后,虽然list提供了这些具有特殊功能的接口,它们也确实有一定的作用,但是实际上这些特殊接口使用频率非常低,包括sort接口(链表排序的效率太低)。

2.4list排序的性能分析

虽然链表排序只能使用list提供的sort接口,而不能使用 algorithm 提供的sort接口,但是其使用频率仍然非常低,这是由于链表排序的效率太低了,我们可以通过对比两组测试数据来直观地感受链表排序的效率。

测试一:vector排序与list排序性能对比

srand((size_t)time(0));constintN=1000000;//100万个数据vector<int>v;v.reserve(N);list<int>lt;for(inti=0;i<N;++i){autoe=rand();v.push_back(e);lt.push_back(e);}//vector sortintbegin1=clock();sort(v.begin(),v.end());intend1=clock();//list sortintbegin2=clock();lt.sort();intend2=clock();printf("vector sort:%d\n",end1-begin1);printf("list sort:%d\n",end2-begin2);

测试二:list直接进行排序与将数据拷贝到vector中使用vector排序后再将数据拷回list中性能对比

srand(time(0));constintN=1000000;//100万个数据list<int>lt1;list<int>lt2;for(inti=0;i<N;++i){autoe=rand();lt1.push_back(e);lt2.push_back(e);}//list sort -- lt1intbegin1=clock();lt1.sort();intend1=clock();// 将数据拷贝到vector中排序,排完以后再拷贝回来 -- lt2intbegin2=clock();vector<int>v;v.reserve(N);for(autoe:lt2)//拷贝{v.push_back(e);}sort(v.begin(),v.end());//排序lt2.assign(v.begin(),v.end());//拷贝intend2=clock();printf("list1 sort:%d\n",end1-begin1);printf("list2 sort:%d\n",end2-begin2);

可以看到,list sort的效率远低于vector sort,甚至于说,直接使用list sort的效率都不如先将数据拷贝到vector中,然后使用vector sort,排序之后再将数据拷贝回list中快;至此,我们也能明白为什么list sort接口使用得非常少了。

注:在工程中,在 release 版本下测试软件或算法性能得到的结果要比在 debug 版本下得到的结果具有参考意义。

三、list迭代器

3.1 迭代器类别

迭代器类别++–+-
Input(只读)✓✗✗✗
Output(只写)✓✗✗✗
Forward(单向)✓✗✗✗
Bidirectional(双向)✓✓✗✗
Random Access(随机)✓✓✓✓

迭代器有很多种类,如上,我们全部列举出来了。vector的迭代器是随机迭代器,支持 ++,–,+,- 多种操作。和vector和string不同,由于结构非连续问题,list的迭代器并非一个原生指针。所以list作为一个双向链表是一个双向迭代器。也就是说,list的迭代器不支持 + 和 - 操作。

由于是双向迭代器,所以list不能采用vector的方式来直接在中间插入数据。

迭代的类型会限制接口的访问:以下我们讲两个例子。首先是标准库的sort,由于sort的底层需要operator[]大量访问数据,所以只支持随机迭代器。

我们看看sort的底层代码:如你所见,使用了 - 操作符,自然不支持。

reverse:reverse的底层有使用 – 操作,所以只能支持双向迭代器。但是这里又要讲一个直接支持和间接支持的问题:

直接支持:如下,reverse直接支持双向迭代器。间接支持:随机迭代器可以用 --,所以reverse也支持随机迭代器的使用。

实际上可以理解为一种包含的关系,双向是一种特殊的单向。随机是一种特殊的双向/单向。

这也可被认为是一种继承 —— 子类是一种特殊的父类。(没有对应的直接类型,但是可以传递其以上任意父类)

3.2list迭代器失效问题

和vector不同,list进行insert操作后并不会产生迭代器失效问题,因为list插入的新节点是动态开辟的,同时由于list每个节点的物理地址是不相关的,所以插入的新节点并不会影响原来其他节点的地址;

但是list在erase之后会发生迭代器失效,因为list删除节点会直接将该节点释放掉,此时我们再访问该节点就会造成越界访问。

3.3list迭代器源码分析

  • list的迭代器底层不是原生指针,而是一个封装指针。原因是结构不连续,无法通过 C/C++ 内置的指针算术进行操作。这个封装指针的解引用、operator++、operator!=等等操作都需修改。

  • 此外,迭代器需要构造函数,因为在调用的时候不能再以一个指针的形态来简单调用了,得构造一个迭代器才能使用。(迭代器本身就是一个封装指针,所以不需要拷贝构造,浅拷贝够用)

//节点定义template<classT>struct__list_node{typedefvoid*void_pointer;void_pointer next;void_pointer prev;T data;};//迭代器定义typedef__list_iterator<T,T&,T*>iterator;typedef__list_iterator<T,constT&,constT*>const_iterator;//迭代器类template<classT,classRef,classPtr>struct__list_iterator{typedef__list_iterator<T,Ref,Ptr>self;typedef__list_node<T>*link_type;//节点的指针link_type node;//类成员变量__list_iterator(link_type x):node(x){}//将节点指针构造为类对象//... 使用运算符重载支持迭代器的各种行为self&operator++(){...}self&operator--(){...}Refoperator*()const{...}};

如上,我们为迭代器专门设计了一个__list_iterator类,然后在类内配合运算符重载、模板等操作使得迭代器支持指针的各种行为(至于这里为什么__list_iterator会有三个模板参数,我们会在后面模拟实现中具体解释,当前我们只需要理解其第一个模板参数T即可)。

相关新闻

车间班组标准化作业提质体系

车间班组标准化作业提质体系

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/21 2:14:49 阅读更多 →
ACORN算法的各种密码分析方法全面盘点

ACORN算法的各种密码分析方法全面盘点

ACORN算法的各种密码分析方法全面盘点ACORN算法作为CAESAR竞赛的最终入选算法之一&#xff0c;自提出以来受到了广泛的密码分析。目前针对它的密码分析方法已相当全面&#xff0c;涵盖了从数学结构到物理实现的多个层面。需要说明的是&#xff0c;现有攻击主要针对简化轮数的AC…

2026/10/21 2:14:49 阅读更多 →
超低功耗物联网芯片:从亚阈值电路到能量采集的全栈实现

超低功耗物联网芯片:从亚阈值电路到能量采集的全栈实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/21 2:14:49 阅读更多 →

最新新闻

Quartz动态设置并发数量

Quartz动态设置并发数量

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/21 2:48:00 阅读更多 →
AI Agent ROI评估指南:从成本结构到收益测算全解析

AI Agent ROI评估指南:从成本结构到收益测算全解析

1. 先别急着算账&#xff0c;想清楚AI Agent的ROI和传统IT项目有什么不同这两年不少公司都开始折腾AI Agent&#xff0c;有的把它接到客服系统&#xff0c;有的让它帮着写代码&#xff0c;还有的用它处理合同和报表。但当你真坐下来算这笔账时&#xff0c;会发现一个尴尬的问题…

2026/10/21 2:48:00 阅读更多 →
基于卷积神经网络的食物图像识别实战:从ResNet迁移学习到部署

基于卷积神经网络的食物图像识别实战:从ResNet迁移学习到部署

简介&#xff1a;基于卷积神经网络的食物图像识别项目完整代码与数据包&#xff0c;面向具备Python基础并希望实践CNN图像分类的开发者与学习者。资源围绕TensorFlow与tf.keras构建食物识别模型&#xff0c;涵盖数据预处理、网络定义、训练评估与预测全流程&#xff0c;并引入迁…

2026/10/21 2:48:00 阅读更多 →
SECS-II/HSMS调试工具实战:从协议原理到模拟器应用

SECS-II/HSMS调试工具实战:从协议原理到模拟器应用

简介&#xff1a;面向半导体制造与MES系统集成场景&#xff0c;这份SECS-II HSMS调试工具模拟器适合设备工程师、上位机开发人员使用。它可模拟客户端与服务端&#xff0c;用于验证自定义程序是否符合SECS-II通信标准&#xff0c;减少现场联调时的错误与返工。压缩包为rar格式&…

2026/10/21 2:48:00 阅读更多 →
eNSP校园网拓扑设计实战:VLAN划分、DHCP配置与静态路由全解析

eNSP校园网拓扑设计实战:VLAN划分、DHCP配置与静态路由全解析

简介&#xff1a;面向计算机网络期末设计、课程设计及eNSP仿真练习的完整项目资料包&#xff0c;围绕某中学校园网络拓扑设计展开。方案包含网络拓扑选型、综合布线系统规划、教学楼/行政楼/宿舍接入设计&#xff0c;以及VLAN划分、IP地址分配、路由协议配置等模拟配置要点&…

2026/10/21 2:48:00 阅读更多 →
思科模拟器6.2:从安装到避坑的完整实战指南

思科模拟器6.2:从安装到避坑的完整实战指南

简介&#xff1a;这是一份思科模拟器6.2安装包&#xff0c;官方名称为Packet Tracer&#xff0c;面向网络技术学习者、思科认证考生以及开展网络实验教学的教师。软件以图形化方式搭建虚拟网络环境&#xff0c;可模拟路由器、交换机、无线接入点、防火墙等设备&#xff0c;支持…

2026/10/21 2:47:00 阅读更多 →

日新闻

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

这段时间硬件圈和AI圈都在传一个事&#xff1a;AMD锐龙AI Max PRO 400系列把最高192GB统一内存搬进了PC形态的产品里&#xff0c;按宣传口径&#xff0c;本地跑320B参数的大家伙不再是纸面概念。我第一反应倒不是“内存真大”&#xff0c;而是这话背后的含金量——它把过去只能…

2026/10/21 0:00:11 阅读更多 →
AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

1. 从“封号重开”这个行为说起&#xff1a;用户到底在追求什么“封号重开”这四个字&#xff0c;放在任何产品语境里都带着一股决绝的味道。一个账号从零开始积累使用记录、调教偏好、沉淀工作流&#xff0c;背后是大量的时间和精力成本。正常情况下&#xff0c;没有人愿意主动…

2026/10/21 0:00:11 阅读更多 →
ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

1. 为什么在ROS里硬刚YOLOv3不是“炫技”&#xff0c;而是工程落地的必经之路我第一次在某高校机器人实验室看到那台搭载Jetson TX2的移动底盘原地打转、反复识别错垃圾桶时&#xff0c;就意识到&#xff1a;单纯跑通darknet_ros demo和真正让YOLOv3在ROS里“干活”&#xff0c…

2026/10/21 0:00:11 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/20 20:43:59 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →