C++ STL list 超详细解析:从接口使用到模拟实现
本文全面讲解 C++ STL 中 list 容器,包含基础介绍、常用接口、迭代器失效、模拟实现、与 vector 对比,附可直接运行的完整代码,适合学习与面试复习。
一、list 基础介绍
list 是 C++ STL 中的序列式容器 ,底层实现为:
带头结点的双向循环链表
核心特点:
- 不支持随机访问(不能用
[]访问) - 任意位置插入/删除效率极高 O(1)
- 插入不会导致迭代器失效,删除仅失效被删节点迭代器
二、list 常用接口使用
1. list 构造函数
| 构造函数 | 功能 |
|---|---|
list() |
构造空 list |
list(n, val) |
构造含 n 个 val 的 list |
list(const list& x) |
拷贝构造 |
list(first, last) |
用迭代器区间构造 |
代码示例
cpp
#include <iostream>
#include <list>
using namespace std;
void test_construct() {
// 空构造
list<int> l1;
// n个val
list<int> l2(5, 10);
// 拷贝构造
list<int> l3(l2);
// 迭代器区间构造
int arr[] = {1,2,3,4,5};
list<int> l4(arr, arr + sizeof(arr)/sizeof(arr[0]));
}
2. 迭代器(iterator)
list 迭代器是双向迭代器 ,只支持 ++/--,不支持 +n/-n。
| 接口 | 功能 |
|---|---|
begin()/end() |
正向迭代器 |
rbegin()/rend() |
反向迭代器 |
代码示例
cpp
void test_iterator() {
list<int> l = {1,2,3,4,5};
// 正向遍历
cout << "正向:";
for (auto it = l.begin(); it != l.end(); ++it) {
cout << *it << " ";
}
cout << endl;
// 反向遍历
cout << "反向:";
for (auto it = l.rbegin(); it != l.rend(); ++it) {
cout << *it << " ";
}
cout << endl;
}
3. 容量与元素访问
| 接口 | 功能 |
|---|---|
empty() |
是否为空 |
size() |
元素个数 |
front() |
首元素引用 |
back() |
尾元素引用 |
代码示例
cpp
void test_capacity() {
list<int> l = {1,2,3};
cout << "empty: " << boolalpha << l.empty() << endl;
cout << "size: " << l.size() << endl;
cout << "front: " << l.front() << endl;
cout << "back: " << l.back() << endl;
}
4. 插入与删除(核心)
| 接口 | 功能 |
|---|---|
push_back(val) |
尾插 |
pop_back() |
尾删 |
push_front(val) |
头插 |
pop_front() |
头删 |
insert(pos, val) |
在 pos 前插入 |
erase(pos) |
删除 pos 位置 |
swap(list) |
交换两个 list |
clear() |
清空 |
代码示例
cpp
void test_modify() {
list<int> l;
// 尾插
l.push_back(1);
l.push_back(2);
// 头插
l.push_front(0);
// insert
auto it = l.begin();
++it;
l.insert(it, 10); // 在 0 后插入 10
// erase
it = l.begin();
l.erase(it);
// 遍历
for (auto x : l) cout << x << " ";
cout << endl;
l.clear();
}
三、list 迭代器失效问题(高频面试)
核心结论
- 插入:不会导致任何迭代器失效
- 删除 :只有被删除节点的迭代器失效,其他不受影响
错误写法
cpp
void TestListIterator1() {
int arr[] = {1,2,3,4,5};
list<int> l(arr, arr+5);
auto it = l.begin();
while (it != l.end()) {
l.erase(it); // it 已失效
++it; // 非法访问!
}
}
正确写法
cpp
void TestListIterator() {
int arr[] = {1,2,3,4,5};
list<int> l(arr, arr+5);
auto it = l.begin();
while (it != l.end()) {
// 后置++:先传值删除,再自增
l.erase(it++);
// 或 it = l.erase(it);
}
}
四、list 模拟实现(精简完整版)
1. 节点结构
cpp
template <class T>
struct ListNode {
T _data;
ListNode* _prev;
ListNode* _next;
ListNode(const T& val = T())
: _data(val)
, _prev(nullptr)
, _next(nullptr)
{}
};
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 temp(*this);
_node = _node->_next;
return temp;
}
// --
Self& operator--() {
_node = _node->_prev;
return *this;
}
Self operator--(int) {
Self temp(*this);
_node = _node->_prev;
return temp;
}
bool operator!=(const Self& s) const { return _node != s._node; }
bool operator==(const Self& s) const { return _node == s._node; }
};
3. 反向迭代器(适配器)
cpp
template <class Iterator>
class ReverseIterator {
public:
Iterator _it;
typedef ReverseIterator<Iterator> Self;
ReverseIterator(Iterator it) : _it(it) {}
auto operator*() {
Iterator temp = _it;
--temp;
return *temp;
}
Self& operator++() {
--_it;
return *this;
}
Self& operator--() {
++_it;
return *this;
}
bool operator!=(const Self& s) const { return _it != s._it; }
};
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;
// 构造空表
list() {
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
// 迭代器
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()); }
// 尾插
void push_back(const T& val) {
Node* newnode = new Node(val);
Node* tail = _head->_prev;
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;
}
// erase
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);
}
// 其他接口:insert、clear、swap、拷贝构造、析构...
private:
Node* _head;
};
五、list 与 vector 核心对比(面试必背)
| 对比项 | vector | list |
|---|---|---|
| 底层结构 | 连续动态数组 | 双向循环链表 |
| 随机访问 | 支持 O(1) | 不支持 O(N) |
| 插入删除 | 需挪动元素 O(N) | 不需挪动 O(1) |
| 空间利用率 | 高、连续、缓存友好 | 低、易产生碎片 |
| 迭代器 | 原生指针 | 封装节点指针 |
| 迭代器失效 | 插入/删除易失效 | 插入不失效,删除仅当前失效 |
| 适用场景 | 频繁访问、少插入 | 频繁插入/删除、不随机访问 |
六、总结
list是双向循环链表,插入删除极快,不支持随机访问。- 迭代器是双向迭代器,删除时注意失效问题。
- 模拟实现核心:节点结构 + 迭代器封装。
- 与
vector互补,根据场景选择。