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

🏝️专栏: 【C++修炼之路】

🌅主页: f狐o狸x

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


目录

一、list的核心特性

二、list的基础使用前提

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

[3.1. 空构造(默认构造)](#3.1. 空构造(默认构造))

[3.2. 填充构造](#3.2. 填充构造)

[3.3. 迭代器范围构造](#3.3. 迭代器范围构造)

[3.4. 拷贝构造](#3.4. 拷贝构造)

四、list的迭代器使用

[4.1. 常用迭代器](#4.1. 常用迭代器)

[4.2. const迭代器](#4.2. const迭代器)

[4.3. 迭代器遍历示例](#4.3. 迭代器遍历示例)

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

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

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

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

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

[5.5. 其他常用函数](#5.5. 其他常用函数)

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

[6.1. 迭代器失效问题](#6.1. 迭代器失效问题)

[6.2. 不可使用STL算法sort](#6.2. 不可使用STL算法sort)

[6.3. 与vector的对比选择](#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):

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

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

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

3.1. 空构造(默认构造)

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

cpp 复制代码
list<int> lst; // 空list,存储int类型元素

3.2. 填充构造

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

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

3.3. 迭代器范围构造

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

cpp 复制代码
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。

cpp 复制代码
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. 迭代器遍历示例

cpp 复制代码
#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)范围的元素。

cpp 复制代码
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的返回值更新迭代器。

cpp 复制代码
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():返回容器最后一个元素的引用,可修改。

cpp 复制代码
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。

cpp 复制代码
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)。

cpp 复制代码
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的返回值更新迭代器:

cpp 复制代码
// 错误写法:迭代器失效后继续使用 
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)任务队列:

cpp 复制代码
 // 任务队列类 
 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基础。

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

相关推荐
松☆2 小时前
Dart 中的常用数据类型详解(含 String、数字类型、List、Map 与 dynamic) ------(2)
数据结构·list
坚持就完事了2 小时前
Java的OOP
java·开发语言
wWYy.2 小时前
C++-集群聊天室(2):muduo网络库
网络·c++
jllllyuz2 小时前
基于MATLAB的锂电池物理对象建模实现
开发语言·matlab
MyBFuture2 小时前
C#数组详解:一维二维与交错数组
开发语言·windows·c#·visual studio·vision pro
程序 代码狂人2 小时前
CentOS7初始化配置操作
linux·运维·开发语言·php
从此不归路2 小时前
Qt5 进阶【13】桌面 Qt 项目架构设计:从 MVC/MVVM 到模块划分
开发语言·c++·qt·架构·mvc
zhangx1234_2 小时前
C语言 数据在内存中的存储
c语言·开发语言
星空露珠2 小时前
速算24点检测生成核心lua
开发语言·数据库·算法·游戏·lua