一、list 介绍
1.1 什么是 list?
list 是 STL 序列式容器 ,底层是带头结点的双向循环链表。
核心特点:
- 任意位置插入 / 删除 = O (1) 效率极高
- 不支持随机访问 (不能用
[]) - 可以双向迭代(
++、--都支持) - 插入不会导致迭代器失效
- 删除只会让被删节点迭代器失效
与 forward_list 对比:
- forward_list:单向链表,只能向前,更省空间、更快
- list:双向链表,更通用、更强大
二、list 的使用(高频接口总结)
1.2.1 list 的构造
cpp
#include <iostream>
#include <list>
using namespace std;
void TestList1()
{
// 1. 空构造
list<int> l1;
// 2. 构造 n 个 val
list<int> l2(5, 10);
// 3. 拷贝构造
list<int> l3(l2);
// 4. 迭代器区间构造
int arr[] = {1,2,3,4,5};
list<int> l4(arr, arr+5);
}
1.2.2 迭代器
begin()/end():正向迭代器rbegin()/rend():反向迭代器
cpp
void TestList2()
{
list<int> l = {1,2,3,4,5};
// 正向遍历
for (auto it = l.begin(); it != l.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
// 反向遍历
for (auto it = l.rbegin(); it != l.rend(); ++it)
{
cout << *it << " ";
}
}
1.2.3 容量操作
cpp
l.empty(); // 是否为空
l.size(); // 元素个数
1.2.4 元素访问
cpp
l.front(); // 第一个元素
l.back(); // 最后一个元素
1.2.5 修改操作
cpp
l.push_back(10); // 尾插
l.pop_back(); // 尾删
l.push_front(20); // 头插
l.pop_front(); // 头删
l.insert(it, 30); // 在 it 前插入
l.erase(it); // 删除 it 位置
l.clear(); // 清空
l.swap(l2); // 交换l与l2
三、list 迭代器失效问题
核心结论
- list 插入:迭代器不会失效
- list 删除:只有被删除节点的迭代器失效
经典错误代码
cpp
// 错误
while (it != l.end())
{
l.erase(it);
++it; // 迭代器已失效,野指针!
}
正确写法:
cpp
// 正确写法1
while (it != l.end())
{
l.erase(it++);
}
// 正确写法2
while (it != l.end())
{
it = l.erase(it);
}
四、list 深度模拟实现(完整版)
4.1 节点结构
cpp
template<class T>
struct ListNode
{
ListNode(const T& val = T())
:_prev(nullptr)
,_next(nullptr)
,_data(val)
{}
private:
ListNode<T>* _prev;
ListNode<T>* _next;
T _data;
};
4.2 正向迭代器
cpp
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)
{}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
Self& operator++()
{
_node = _node->_next;
return *this;
}
Self operator++(int)
{
Self tmp(*this);
_node = _node->_next;
return tmp;
}
Self& operator--()
{
_node = _node->_prev;
return *this;
}
bool operator!=(const Self& it) const
{
return _node != it._node;
}
};
4.3 反向迭代器(适配器)
cpp
template<class Iterator>
class ReverseIterator
{
public:
typedef typename Iterator::Ref Ref;
typedef ReverseIterator<Iterator> Self;
ReverseIterator(Iterator it)
:_it(it)
{}
Ref operator*()
{
Iterator temp(_it);
--temp;
return *temp;
}
Self& operator++()
{
--_it;
return *this;
}
bool operator!=(const Self& s) const
{
return _it != s._it;
}
private:
Iterator _it;
};
4.4 list 主体
cpp
template<class T>
class list
{
typedef ListNode<T> Node;
public:
typedef ListIterator<T, T&, T*> iterator;
typedef ListIterator<T, const T&, const T*> const_iterator;
typedef ReverseIterator<iterator> reverse_iterator;
iterator begin() { return iterator(_head->_next); }
iterator end() { return iterator(_head); }
reverse_iterator rbegin() { return reverse_iterator(end()); }
reverse_iterator rend() { return reverse_iterator(begin()); }
list()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
void push_back(const T& x)
{
insert(end(), x);
}
iterator insert(iterator pos, const T& x)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(x);
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
return iterator(newnode);
}
iterator erase(iterator pos)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
prev->_next = next;
next->_prev = prev;
delete cur;
return iterator(next);
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
}
private:
Node* _head;
};
五、list 与 vector 对比
表格
| 维度 | vector | list |
|---|---|---|
| 底层结构 | 连续数组 | 双向循环链表 |
| 随机访问 | 支持 O (1) | 不支持 |
| 插入删除 | O(N) | O(1) |
| 空间利用率 | 高 | 低 |
| 迭代器 | 原生指针 | 封装迭代器 |
| 迭代器失效 | 容易失效 | 插入不失效 |
| 适用场景 | 随机访问、少插入 | 频繁插入删除 |
六、list 工程实际使用场景
list 在实际开发中非常常用,主要用于:
-
需要频繁在任意位置插入 / 删除
- 消息队列
- 任务调度链表
- 撤销 / 重做栈
- 游戏对象管理列表
-
对插入性能要求极高
- 后端事件链表
- 内存池管理
- LRU 缓存结构
-
不需要随机访问
- 日志管理
- 异步任务队列
一句话总结:需要频繁插入删除,不关心随机访问 → 用 list
七、总结
- list = 带头双向循环链表
- 插入删除 O (1),不支持随机访问
- 迭代器是封装类型,不是原生指针
- 插入不失效,删除只失效当前迭代器
- 模拟实现核心:节点 + 迭代器封装 + 反向迭代器适配器
- 工程中用于频繁插入删除的队列、链表、任务系统