C++ STL list 容器基于双向循环链表实现,支持 O(1) 时间复杂度的任意位置插入与删除,但不支持随机访问。本文详细梳理了 list 的核心接口,包括构造、迭代器操作、容量管理及元素访问,重点解析了迭代器失效规则及正确清理方法。通过手写模拟实现,深入剖析了节点类设计、迭代器模板技巧以及内存管理细节,并与 vector 进行了底层结构对比,帮助开发者理解不同场景下的容器选型策略。
观心0 浏览
C++ STL list 容器详解:使用与模拟实现
1. list 的介绍及使用
1.1 list 的介绍
在 C++ STL 家族里,list 是个很特别的成员。它是一个带头结点的双向循环链表。和 vector 不同,list 在任意位置插入和删除元素的时间复杂度都是 O(1),但它不支持随机访问,不能通过下标直接获取元素。
1.2 list 的使用
list 提供了丰富的接口,下面梳理一些常见且重要的用法。
1.2.1 list 的构造
构造函数
接口说明
list(size_type n, const value_type& val)
构造包含 n 个值为 val 的元素的 list
list()
构造空的 list
list(const list& x)
拷贝构造函数
list(InputIterator first, InputIterator last)
用区间 [first, last) 中的元素构造 list
1.2.2 list 迭代器的使用
迭代器本质上是对底层节点指针的封装。
begin():返回指向第一个元素的迭代器
end():返回指向最后一个元素下一个位置的迭代器
rbegin():返回指向最后一个元素的反向迭代器(即 end() 位置)
rend():返回指向第一个元素前一个位置的反向迭代器(即 begin() 位置)
需要留意的是:begin 和 end 是正向迭代器,++ 向后移动;rbegin 和 rend 是反向迭代器,++ 向前移动。
1.2.3 list capacity
函数声明
接口说明
empty()
检测 list 是否为空
size()
返回 list 中有效节点的个数
1.2.4 list element access
函数声明
接口说明
front()
返回第一个节点中值的引用
back()
返回最后一个节点中值的引用
1.2.5 list modifiers
函数声明
接口说明
push_front
在 list 首元素前插入值为 val 的元素
pop_front
删除 list 中第一个元素
push_back
在 list 尾部插入值为 val 的元素
pop_back
删除 list 中最后一个元素
insert
在 position 位置插入值为 val 的元素
erase
删除 position 位置的元素
swap
交换两个 list 中的元素
clear
清空 list 中的有效元素
1.2.6 list 的迭代器失效
由于 list 的底层是双向循环链表,插入操作不会导致迭代器失效。只有在删除元素时,指向被删除节点的迭代器才会失效,其他迭代器不受影响。
常见的错误写法如下:
while (it != l.end()) {
l.erase(it); // it 失效
++it; // 错误,it 已无效
}
2. list 的模拟实现
2.1 基本结构
2.1.1 节点类 (list_node)
这是 list 的基础节点结构,采用双向链表设计,每个节点包含数据及前后指针。
template<classT> classlist_node {
public:
T _data;
list_node<T>* _next;
list_node<T>* _prev;
list_node(const T& data = T()) :_data(data), _next(nullptr), _prev(nullptr) {}
};