前言:在上一篇文章中,我们深入探讨了vector的底层原理。今天,让我们一起来认识另一个重要容器 - list。理解一个容器的最佳方式是从它的接口入手,下面我们就从list的基本使用方法开始讲解。
对于一个容器我们首先要了解它的底层长什么样,才能更好的去认识这个容器。像vector它的底层实际上就是一个顺序表,而C++的list的底层实际上就是一个带头双向链表。

以上就是整个链表的结构,但要注意这里的结点不是像vector那样是连续的,list每个结点之间是不连续的!接下来就来看看list的一些核心接口。
只要是学习容器我们一般都从它的构造相关的接口开始看,然后再逐步过渡到,迭代器,增删查改,算法这些接口。
构造函数接口说明
构造函数签名 | 功能描述 |
|---|---|
list(size_type n, const value_type& val = value_type()) | 构造一个包含n个值为val的元素的list,若val未指定则使用默认值 |
list() | 构造一个空的list |
list(const list& x) | 拷贝构造函数,通过另一个list对象x构造新list |
list(InputIterator first, InputIterator last) | 通过迭代器区间[first, last)中的元素构造list |
注意事项:
val参数未提供时,使用value_type的默认构造值(如int为0,类类型调用默认构造函数)。[first, last)需为有效的输入迭代器范围,左闭右开。list与x的元素完全独立,深拷贝。代码示例:
void list_test1()
{
//默认构造
list<int> lit1;
//n个val构造
list<int> lit2(10, 1);
//初始化列表构造
list<int> lt3 = { 1,2,3,4,5,6,1,1,1,1};
//拷贝构造 lit4(lit3)
list<int> lit4(lit3);
//迭代器区间构造
//list<int> lit5(lit2.begin()+3, lit2.end());list的迭代器不支持+这种操作后面会讲
list<int> lit5(lit2.begin(), lit2.end());
int a[] = { 1,2,3,4,5,6,7 };
list<int> lit6(a, a + 3);
for (auto e : lit5)
{
cout << e<<" ";
}
cout << endl;
}函数名 | 返回值类型 | 描述 |
|---|---|---|
begin | 迭代器 | 返回指向容器第一个元素的迭代器 |
end | 迭代器 | 返回指向容器末尾(最后一个元素的下一个位置)的迭代器 |
rbegin | 反向迭代器 (reverse_iterator) | 返回指向容器最后一个元素的反向迭代器(即end位置) |
rend | 反向迭代器 (reverse_iterator) | 返回指向容器第一个元素前一个位置的反向迭代器(即begin位置) |
对于list迭代器的使用与前面类似这里就不给代码示例了。实际上C++设计的这种迭代器就是一种展现C++封装的特性,这使得我们不用去关系各种容器的底层是怎么封装的,只管使用这些迭代器就行。 因为这些迭代器虽然在底层的实现上可能不同但是在使用上功能和用法都是相似的,所以这也是迭代器更底层解耦的体现!
list迭代器的补充:

看到这里相信很多读者朋友又会有疑问:为什么要将这些迭代器区分开?不同容器的迭代器之间有什么不同?是按照什么标准来区分的?下面就通过一个表格来回答这些问题:
迭代器类型 | 支持的操作 | 适用的数据结构 | 特性说明 |
|---|---|---|---|
单向迭代器 | ++ | 单链表、哈希表(如 unordered_map) | 仅能沿一个方向移动,用于遍历单向结构,无法回退。 |
双向迭代器 | ++、-- | 红黑树(如 map)、双向链表(如 list) | 可双向移动,支持向前和向后遍历,适用于需要双向操作的场景。 |
随机迭代器 | ++、--、+、-、[] | string、vector、双端队列 deque | 支持随机访问,可通过下标或指针算术直接访问任意位置元素,迭代灵活性最高。 |
注意事项:
++、*、!= 这些通用的操作!list<int> lit5(lit2.begin()+3, lit2.end());再来看看这段代码,之所以不能这样使用是因为list首先的迭代器是双向迭代器,双向迭代器不支持
+这种操作!
函数名 | 返回值类型 | 功能描述 |
|---|---|---|
empty | bool | 检测list是否为空,是返回true,否则返回false |
size | size_t(或等效整型) | 返回list中有效节点的个数 |
front | T&(模板类型引用) | 返回list的第一个节点中存储值的引用 |
back | T&(模板类型引用) | 返回list的最后一个节点中存储值的引用 |
注意事项:
front和back通常要求list非空,否则可能引发未定义行为(如断言错误或异常)。size的时间复杂度需根据实现确定(如O(1)或O(n))。const重载版本(如const T& front() const)。代码示例:
void list_test2()
{
list<int> lit1(10,1);
lit1.push_back(10);
//size为11 就是有效结点个数
cout << lit1.size() << endl;
if (!lit1.empty())
{
for (auto e : lit1)
{
cout << e << " ";
}
}
cout << endl;
lit1.front()=2;//返回的是首元素的引用可以修改
lit1.back() = 100;//返回的是最后一个元素的引用
for (auto e : lit1)
{
cout << e << " ";
}
}操作 | 语法示例 | 功能描述 | 时间复杂度 |
|---|---|---|---|
push_front | list.push_front(val) | 在链表头部插入值为 val 的元素 | O ( 1 ) O(1) O(1) |
pop_front | list.pop_front() | 删除链表第一个元素 | O ( 1 ) O(1) O(1) |
push_back | list.push_back(val) | 在链表尾部插入值为 val 的元素 | O ( 1 ) O(1) O(1) |
pop_back | list.pop_back() | 删除链表最后一个元素 | O ( 1 ) O(1) O(1) |
insert | list.insert(pos, val) | 在迭代器 pos 位置插入值为 val 的元素 | O ( 1 ) O(1) O(1) |
erase | list.erase(pos) | 删除迭代器 pos 位置的元素 | O ( 1 ) O(1) O(1) |
swap | list1.swap(list2) | 交换两个链表的所有元素 | O ( 1 ) O(1) O(1) |
clear | list.clear() | 清空链表中所有有效元素 | O ( n ) O(n) O(n) |
pop_frontlist.pop_front()删除链表第一个元素
push_backlist.push_back(val)在链表尾部插入值为 val 的元素
pop_backlist.pop_back()删除链表最后一个元素
insertlist.insert(pos, val)在迭代器 pos 位置插入值为 val 的元素
eraselist.erase(pos)删除迭代器 pos 位置的元素
swaplist1.swap(list2)交换两个链表的所有元素
clearlist.clear()清空链表中所有有效元素
注意事项:
insert 和 erase 操作需传入有效的迭代器位置。pop_front 和 pop_back 在空链表上调用可能导致未定义行为。clear 的时间复杂度为 ,其余操作均为常数时间
。
代码示例:
void list_test4()
{
list<int> lit1;
lit1.push_back(2);//尾插
lit1.push_back(20);
lit1.push_front(10);//头插
lit1.push_front(100);
for (auto e : lit1)
{
cout << e << " ";
}
cout << endl;
lit1.pop_back();//尾删
lit1.pop_front();//头删
/*for (auto e : lit1)
{
cout << e << " ";
}
cout << endl;*/
//在pos位置插入
auto it = find(lit1.begin(), lit1.end(), 20);
lit1.insert(it, 200);//插入200
auto it2 = find(lit1.begin(), lit1.end(), 200);
//lit1.erase(it2);//删除200 引发迭代器失效!
//将迭代器更新一下 能避免失效
it2=lit2.erase(it2);
for (auto e : lit1)
{
cout << e << " ";
}
cout << endl;
list<int> lit2(10,2);
list<int> lit3(10,20);
lit2.swap(lit3);
for (auto e : lit2)
{
cout << e << " ";
}
cout << endl;
}迭代器失效问题:
与
vector一样list在执行删除操作时也会引发迭代器失效。这里我们先简单的将list的迭代器理解成指针,那么迭代器失效的原因就是指向了被删除的结点,此时的迭代器就相当于一个野指针!而由于链表是一个循环链表所以插入是不会引起迭代器失效的。所以与vector一样使用erase的返回值更新迭代器就能避免失效的问题!
以上就是本篇文章的所有内容了,衷心感谢您的阅读!这篇文章凝聚了大量心血,如果觉得有帮助,请不吝点赞支持。有任何疑问,欢迎随时私信交流探讨。