【C++修炼之路】C++ list容器基本用法详解

【C++修炼之路】C++ list容器基本用法详解
Alt

🏝️专栏: 【C++修炼之路】
🌅主页: f狐o狸x

“追风赶月莫停留,平芜尽处是春山”


目录

一、list的核心特性

二、list的基础使用前提

三、list的构造函数(4种常用方式)

3.1. 空构造(默认构造)

3.2. 填充构造

3.3. 迭代器范围构造

3.4. 拷贝构造

四、list的迭代器使用

4.1. 常用迭代器

4.2. const迭代器

4.3. 迭代器遍历示例

五、list的常用成员函数(核心)

5.1. 元素插入(push_back、push_front、insert)

5.2. 元素删除(pop_back、pop_front、erase、clear)

5.3. 元素访问与修改(front、back)

5.4. 容器大小与判断(size、empty、resize)

5.5. 其他常用函数

六、list的常见误区与注意事项

6.1. 迭代器失效问题

6.2. 不可使用STL算法sort

6.3. 与vector的对比选择

七、实战案例:list实现简单任务队列

总结


在C++ STL(标准模板库)中,list是一种基于双向链表实现的序列式容器,它与vector、deque并称为常用的线性容器,但因底层结构不同,具备独特的优势与适用场景。本文将从list的核心特性出发,逐步讲解其构造、迭代器、常用成员函数及实战用法,适合C++初学者快速上手。

一、list的核心特性

list的底层是双向链表,每个节点包含数据域、前驱指针和后继指针,节点之间通过指针关联,不占用连续的内存空间。这种结构决定了它的核心特性:

  • 插入/删除高效:在list的任意位置(头部、尾部、中间)插入或删除元素时,仅需修改节点的指针指向,时间复杂度为O(1)(前提是已找到目标位置),无需像vector那样移动大量元素。
  • 随机访问低效:无法通过下标[]直接访问元素,必须通过迭代器逐步遍历,访问第n个元素的时间复杂度为O(n),因为需要从头部或尾部逐个移动指针。
  • 内存灵活:节点动态分配内存,无需提前预留空间,内存利用率较高,避免了vector扩容时的内存浪费。
  • 支持双向遍历:提供双向迭代器(bidirectional iterator),可正向、反向遍历容器。

适用场景:适合频繁进行插入、删除操作,且无需频繁随机访问元素的场景,例如任务队列、频繁修改的链表结构等。

二、list的基础使用前提

使用list容器前,必须包含对应的头文件,并引入std命名空间(或显式使用std::list):

#include <list> // 必须包含的头文件 using namespace std; // 简化写法,否则需写std::list

三、list的构造函数(4种常用方式)

list提供多种构造方式,可根据需求灵活选择,以下是最常用的4种:

3.1. 空构造(默认构造)

创建一个空的list,无任何元素,初始大小为0。

list<int> lst; // 空list,存储int类型元素

3.2. 填充构造

创建一个包含n个相同值的list,值可指定(默认值为元素类型的默认构造值,如int默认0)。

// 方式1:创建包含5个元素,每个元素值为10的 list list<int> lst1(5, 10); // 方式2:创建包含3个元素,每个元素为int默认值0 list<int> lst2(3);

3.3. 迭代器范围构造

通过其他容器的迭代器范围,构造一个新的list(复制该范围的所有元素)。

vector<int> vec = {1,2,3,4,5}; // 复制vec的所有元素(从begin到end),构造list list<int> lst3(vec.begin(), vec.end()); // 复制vec的前3个元素(从begin到begin+3) list<int> lst4(vec.begin(), vec.begin()+3);

3.4. 拷贝构造

通过另一个list,复制其所有元素构造新的list。

list<int> lst5(5, 10); list<int> lst6(lst5); // 拷贝lst5的所有元素,lst6与lst5完全一致

四、list的迭代器使用

list的迭代器是双向迭代器,支持++(正向移动)、--(反向移动)操作,但不支持+、-(如it+2)操作(区别于vector的随机访问迭代器)。常用迭代器分为4类:

4.1. 常用迭代器

  • begin():返回指向容器第一个元素的迭代器(非const,可修改元素)。
  • end():返回指向容器末尾(最后一个元素的下一个位置)的迭代器,不指向具体元素。
  • rbegin():返回反向迭代器,指向容器最后一个元素(正向遍历的逆序起点)。
  • rend():返回反向迭代器,指向容器第一个元素的前一个位置。

4.2. const迭代器

用于只读场景,防止通过迭代器修改元素,对应const_begin()、const_end()、const_rbegin()、const_rend()。

4.3. 迭代器遍历示例

#include <list> #include <iostream> using namespace std; int main() { list<int> lst = {1,2,3,4,5}; // 1. 正向遍历(非const迭代器) cout << "正向遍历:"; for (list<int>::iterator it = lst.begin(); it != lst.end(); it++) { cout << *it << " "; // *it 获取迭代器指向的元素 } cout << endl; // 2. 反向遍历(反向迭代器) cout << "反向遍历:"; for (list<int>::reverse_iterator it = lst.rbegin(); it != lst.rend(); it++) { cout << *it << " "; } cout << endl; // 3. const迭代器(只读) cout << "const迭代器遍历(只读):"; for (list<int>::const_iterator it = lst.cbegin(); it != lst.cend(); it++) { // *it = 10; // 报错,const迭代器无法修改元素 cout << *it << " "; } cout << endl; return 0; }

输出结果:

五、list的常用成员函数(核心)

list的成员函数主要用于元素的增、删、改、查及容器的基础操作,以下是高频使用的函数,搭配实例说明。

5.1. 元素插入(push_back、push_front、insert)

push_back(elem):在容器尾部插入一个元素elem,时间复杂度O(1)。push_front(elem):在容器头部插入一个元素elem,时间复杂度O(1)(vector无此函数)。insert(pos, elem):在迭代器pos指向的位置插入elem,返回指向插入元素的迭代器,时间复杂度O(1)。insert(pos, n, elem):在pos位置插入n个elem元素。insert(pos, beg, end):在pos位置插入另一个容器[beg, end)范围的元素。
list<int> lst; // 尾部插入 lst.push_back(10); lst.push_back(20); // 头部插入 lst.push_front(5); // 迭代器指向第二个元素(值为10),插入15 list<int>::iterator it = ++lst.begin(); lst.insert(it, 15); // 插入3个30,位置在开头 lst.insert(lst.begin(), 3, 30); // 遍历结果:30 30 30 5 15 10 20

5.2. 元素删除(pop_back、pop_front、erase、clear)

pop_back():删除容器尾部元素,无返回值,时间复杂度O(1)。pop_front():删除容器头部元素,无返回值,时间复杂度O(1)(vector无此函数)。erase(pos):删除迭代器pos指向的元素,返回指向删除元素下一个位置的迭代器,时间复杂度O(1)。erase(beg, end):删除[beg, end)范围的元素,返回指向删除范围下一个位置的迭代器。clear():清空容器所有元素,容器大小变为0,时间复杂度O(n)。

⚠️ 注意:删除元素后,指向该元素的迭代器会失效,后续不可再使用,需通过erase的返回值更新迭代器。

list<int> lst = {5,15,10,20}; // 删除尾部元素(20) lst.pop_back(); // 删除头部元素(5) lst.pop_front(); // 删除迭代器指向的元素(15) list<int>::iterator it = lst.begin(); it = lst.erase(it); // it更新为指向10 // 删除所有元素 lst.clear(); // 此时lst为空,size()为0

5.3. 元素访问与修改(front、back)

list不支持下标访问,只能通过front()和back()访问头部、尾部元素:

front():返回容器第一个元素的引用,可修改(非const容器)。back():返回容器最后一个元素的引用,可修改。
list<int> lst = {1,2,3}; cout << "头部元素:" << lst.front() << endl; // 输出1 cout << "尾部元素:" << lst.back() << endl; // 输出3 // 修改头部、尾部元素 lst.front() = 10; lst.back() = 30; // 此时list为{10,2,30}

5.4. 容器大小与判断(size、empty、resize)

size():返回容器中元素的个数,时间复杂度O(1)(C++11后)。empty():判断容器是否为空,为空返回true,否则返回false,时间复杂度O(1)。resize(n):将容器大小调整为n,若n>原大小,新增元素为默认值;若n<原大小,删除多余元素。resize(n, elem):调整大小为n,新增元素为elem。
list<int> lst = {1,2,3}; cout << "元素个数:" << lst.size() << endl; // 输出3 cout << "是否为空:" << lst.empty() << endl; // 输出0(false) lst.resize(5, 10); // 调整为5个元素,新增2个10,结果{1,2,3,10,10} lst.resize(2); // 调整为2个元素,删除多余元素,结果{1,2}

5.5. 其他常用函数

sort():对list元素进行排序,默认升序,时间复杂度O(nlogn)(注意:list自带sort成员函数,不可使用STL算法sort,因为STL sort要求随机访问迭代器)。reverse():反转容器中所有元素的顺序,时间复杂度O(n)。unique():删除容器中相邻的重复元素(需先排序,否则仅删除连续重复项),时间复杂度O(n)。merge(lst):将另一个已排序的list lst合并到当前已排序的list中,合并后仍有序,时间复杂度O(n+m)。
list<int> lst = {3,1,4,2,2,5,5,5}; // 排序(升序) lst.sort(); // 结果{1,2,2,3,4,5,5,5} // 反转 lst.reverse(); // 结果{5,5,5,4,3,2,2,1} // 去重(需先排序,这里重新排序后去重) lst.sort(); lst.unique(); // 结果{1,2,3,4,5}

六、list的常见误区与注意事项

6.1. 迭代器失效问题

list插入元素时,仅修改节点指针,迭代器不会失效(指向插入位置前后的迭代器仍可用);但删除元素时,指向被删除元素的迭代器会失效,其他迭代器不受影响。因此删除元素后,需通过erase的返回值更新迭代器:

// 错误写法:迭代器失效后继续使用 for (auto it = lst.begin(); it != lst.end(); it++) { if (*it == 2) { lst.erase(it); // it失效,后续++操作报错 } } // 正确写法:用erase返回值更新迭代器 for (auto it = lst.begin(); it != lst.end(); ) { if (*it == 2) { it = lst.erase(it); // 更新it为下一个有效迭代器 } else { it++; } }

6.2. 不可使用STL算法sort

STL中的sort算法(#include <algorithm>)要求迭代器支持随机访问,而list的迭代器是双向迭代器,不满足要求,因此必须使用list自带的sort成员函数。

6.3. 与vector的对比选择

很多初学者会混淆list和vector,两者核心区别及选择建议如下:

特性

list

vector

底层结构

带头双向循环链表

动态数组

随机访问

不支持(O(n))

支持(O(1))

插入/删除(中间)

高效(O(1))

低效(O(n))

内存占用

节点额外存储指针,内存开销大

连续内存,开销小

适用场景

频繁插入/删除,无需随机访问

频繁随机访问,插入/删除多在尾部

七、实战案例:list实现简单任务队列

利用list的头部删除、尾部插入高效的特性,实现一个简单的先进先出(FIFO)任务队列:

 // 任务队列类 class TaskQueue { public: list<string> taskList; // 存储任务的list public: // 加入任务(尾部插入) void pushTask(const string& task) { taskList.push_back(task); cout << "任务加入:" << task << endl; } // 执行任务(头部删除) void executeTask() { if (taskList.empty()) { cout << "无任务可执行!" << endl; return; } string task = taskList.front(); taskList.pop_front(); cout << "执行任务:" << task << endl; } // 查看队列大小 int getTaskCount() const { return taskList.size(); } }; int main() { TaskQueue q; q.pushTask("完成C++ list博客"); q.pushTask("学习STM32新建工程"); q.pushTask("整理嵌入式笔记"); cout << "当前任务数:" << q.getTaskCount() << endl; q.executeTask(); q.executeTask(); q.executeTask(); q.executeTask(); // 无任务 return 0; }

输出结果:

除此之外,我们还可以用list实现栈、队列、双端队列等数据结构,感兴趣的话可以去看煮包的数据结构专栏~

总结

本文讲解了C++ list容器的核心用法,包括构造函数、迭代器、常用成员函数及实战案例,重点突出了list基于双向链表的特性与适用场景。对于初学者而言,需牢记list与vector的区别,根据实际需求选择合适的容器;同时注意迭代器失效问题,避免编程错误。

后续可深入学习list的进阶用法,如自定义排序规则、与其他STL容器的配合使用等,逐步夯实C++ STL基础。

都看到这里啦,留下你滴三连吧~

Read more

AI视频制作完整流程指南

在AI技术飞速发展的今天,视频创作不再是专业团队的专属领域。本文将带你深入了解AI视频制作的完整流程,从最初的创意构思到最终的成品输出,让你也能轻松制作出高质量的AI视频作品。 目录 引言:AI视频制作的革命 第一步:内容生成 - 让AI理解你的创意 为什么内容生成是第一步? 大模型能为你做什么? 实战示例:从简单到详细 推荐的大语言模型 实用技巧 第二步:画面生成 - 从文字到视觉 2.1 分镜画面生成(AI绘图) 2.2 关键帧生成视频(图生视频) 第三步:剪辑 - 赋予视频生命 常用剪辑软件对比 常用剪辑手法详解 剪辑节奏控制 AI辅助剪辑功能 第四步:配音 - 让视频开口说话 AI配音软件对比 配音制作流程 进阶技巧:声音克隆 第五步:其他优化 - 完善细节

By Ne0inhk
AI调参技巧:贝叶斯优化Optuna

AI调参技巧:贝叶斯优化Optuna

AI调参技巧:贝叶斯优化Optuna 📝 本章学习目标:本章聚焦性能优化,帮助读者提升模型效率。通过本章学习,你将全面掌握"AI调参技巧:贝叶斯优化Optuna"这一核心主题。 一、引言:为什么这个话题如此重要 在人工智能快速发展的今天,AI调参技巧:贝叶斯优化Optuna已经成为每个AI从业者必须掌握的核心技能。Python作为AI开发的主流语言,其丰富的生态系统和简洁的语法使其成为机器学习和深度学习的首选工具。 1.1 背景与意义 💡 核心认知:Python在AI领域的统治地位并非偶然。其简洁的语法、丰富的库生态、活跃的社区支持,使其成为AI开发的不二之选。掌握Python AI技术栈,是进入AI行业的必经之路。 从NumPy的高效数组运算,到TensorFlow和PyTorch的深度学习框架,Python已经构建了完整的AI开发生态。据统计,超过90%的AI项目使用Python作为主要开发语言,AI岗位的招聘要求中Python几乎是标配。 1.2 本章结构概览 为了帮助读者系统性地掌握本章内容,我将从以下几个维度展开: 📊 概念解析 → 原理推导 → 代

By Ne0inhk
ToDesk 全新 ToClaw,正在把电脑交给AI去操作

ToDesk 全新 ToClaw,正在把电脑交给AI去操作

这两年,AI 工具层出不穷,但大多数产品还停留在“能回答、会生成”的阶段:帮你写一段话、搜一份资料、整理一个思路,真正到了执行层,还是得你自己坐回电脑前,一个软件一个软件地点、一项任务一项任务地做。 这也是很多人对 AI 的真实感受——它会说,但不一定真能干活。而 ToDesk 新上线的 ToClaw,想解决的正是这个问题。 一、ToClaw 是什么? ToClaw 是一款基于 OpenClaw 深度定制、并与远程控制运行时深度结合的 AI 助手。它最大的不同,不只是“懂你说什么”,而是能直接在你的电脑上执行操作。 你只需要一句话,它就可以在电脑端完成对应动作:打开软件、点击按钮、填写表单、拖拽文件、整理资料、生成表格、汇总信息……很多原本需要人守在电脑前操作的工作,现在都可以交给 ToClaw

By Ne0inhk
【保姆级】无需公网 IP!Windows 本地一键部署 OpenClaw,10 分钟打造你的飞书 AI 数字员工

【保姆级】无需公网 IP!Windows 本地一键部署 OpenClaw,10 分钟打造你的飞书 AI 数字员工

目录 写在前面 OpenClaw 是什么? 蓝耘平台是什么?与 OpenClaw 的关系 步骤一:极速安装,一行命令搞定环境 步骤二:启动向导,初始化配置参数 步骤 三:注入灵魂,获取蓝耘MaaS API Key 步骤四:打通渠道,搭建飞书长连接桥梁 步骤五:引擎点火,启动核心网关服务 步骤六:仪表盘检阅,后台状态可视化 步骤七:实战演练,验证智能交互效果 快速排错提示 写在末尾 写在前面 本文面向:想在 Windows 本地(PowerShell)一键部署 OpenClaw,使用蓝耘MaaS作为大模型,并通过飞书长连接模式实现 AI 机器人的用户。 内容涵盖:从零开始安装配置、对接飞书机器人、验证与排错的完整流程,

By Ne0inhk