
博主名称:deM0one ⭐️C++专栏: C++
文章目录
- [C++ STL list 容器底层原理:从零实现一个类 STL 的 list](#C++ STL list 容器底层原理:从零实现一个类 STL 的 list)
-
- 一、引言
- 二、迭代器类型与性能特性
-
- [2.1 迭代器类型](#2.1 迭代器类型)
- [2.2 性能特点](#2.2 性能特点)
- [2.3 支持双向迭代器的容器](#2.3 支持双向迭代器的容器)
- [三、vector 的成员类型详解](#三、vector 的成员类型详解)
- 四、迭代器分类体系
- [五、list 的内部结构设计](#五、list 的内部结构设计)
-
- [5.1 循环双向链表 + 哨兵节点](#5.1 循环双向链表 + 哨兵节点)
- [5.2 push_back 实现](#5.2 push_back 实现)
- 六、迭代器的封装与实现
-
- [6.1 普通人的写法(不封装迭代器)](#6.1 普通人的写法(不封装迭代器))
- [6.2 仿 STL 的迭代器封装](#6.2 仿 STL 的迭代器封装)
- 七、总结
- 附录:参考资料
C++ STL list 容器底层原理:从零实现一个类 STL 的 list
一、引言
- C++ STL(Standard Template Library)是 C++ 标准库的核心组件
list作为双向链表容器,与vector等顺序容器有本质区别- 本文将从迭代器类型、内部结构、关键操作实现、迭代器封装四个维度,深度解析
list的工作原理
二、迭代器类型与性能特性
2.1 迭代器类型
| 类型 | 说明 |
|---|---|
iterator |
普通迭代器,支持读写 |
reverse_iterator |
反向迭代器,从尾部向前遍历 |
const_iterator |
常量迭代器,只读访问 |
const_reverse_iterator |
常量反向迭代器 |
2.2 性能特点
- 支持双向遍历 :可通过
++和--操作前后移动 - 不支持随机访问 :无
operator[],不能通过下标直接访问元素 - 时间复杂度:
- 插入/删除:O(1)(已知位置)
- 查找:O(n)
2.3 支持双向迭代器的容器
list、map、multimapset、multisetvector、string、deque(这些支持随机访问迭代器)
注意:STL 从 C++98 起就支持上述迭代器类型。
三、vector 的成员类型详解
以 std::vector<T, Alloc> 为例,其成员类型定义如下:
| 成员类型 | 定义 |
|---|---|
value_type |
模板第一个参数 T |
allocator_type |
模板第二个参数 Alloc |
reference |
allocator_type::reference |
const_reference |
allocator_type::const_reference |
pointer |
allocator_type::pointer |
const_pointer |
allocator_type::const_pointer |
iterator |
指向 value_type 的随机访问迭代器 |
const_iterator |
指向 const value_type 的随机访问迭代器 |
reverse_iterator |
reverse<iterator> |
size_type |
无符号整数类型 |
关键点 :
vector的迭代器是随机访问迭代器 ,而list的迭代器仅为双向迭代器。
四、迭代器分类体系
Input ← Output ← Forward ← Bidirectional ← Random Access
| 迭代器类型 | 能力说明 |
|---|---|
| Input Iterator | 只读、单向、只能前进一次 |
| Forward Iterator | 可读写、可多次遍历同一元素 |
| Bidirectional Iterator | 可前可后移动(list、map 使用) |
| Random Access Iterator | 支持任意位置跳跃(vector、deque 使用) |
五、list 的内部结构设计
5.1 循环双向链表 + 哨兵节点
list 内部采用循环双向链表结构,并设置一个**哨兵节点(sentinel)**作为头节点。
cpp
protected:
link_type node; // 哨兵节点指针
public:
list() { empty_initialize(); }
void empty_initialize() {
node = get_node(); // 分配哨兵节点
node->next = node; // 自循环
node->prev = node;
}
设计意义:哨兵节点简化了边界处理,使得空链表和非空链表的处理逻辑保持一致。
5.2 push_back 实现
cpp
void push_back(const T& x) {
Node* newnode = new Node(x);
Node* tail = _head->prev; // 当前尾节点
tail->next = newnode; // 原尾部指向新节点
newnode->prev = tail; // 新节点指回原尾部
newnode->next = _head; // 新节点指向哨兵
_head->prev = newnode; // 哨兵指回新节点
}
插入过程图示:
tail newnode
↓ ↓
[head] ←── 1 ←── 2 ←── 3 ←── 4
↑ ↑
head newnode
- 在链表尾部插入新节点
newnode - 更新原尾节点
tail的next指向新节点 - 新节点的
prev指回tail - 新节点的
next指向head head的prev指向新节点 → 形成闭环
六、迭代器的封装与实现
6.1 普通人的写法(不封装迭代器)
cpp
typedef list_iterator<T> iterator;
iterator begin() {
iterator it(_head->next);
return iterator(_head->next);
}
iterator end() {
return _head; // 返回哨兵节点
}
问题:直接暴露底层节点指针,缺乏封装,不符合 STL 规范。
6.2 仿 STL 的迭代器封装
cpp
template<class T>
struct list_iterator {
typedef list_node<T> Node;
typedef list_iterator<T> Self;
Node* _node;
list_iterator(Node* node) : _node(node) {}
// 解引用操作
T& operator*() {
return _node->_data;
}
// 前置递增
Self& operator++() {
_node = _node->next;
return *this;
}
// 不等比较
bool operator!=(const Self& s) {
return _node != s._node;
}
};
迭代器遍历图示:
pos
↓
[head] ←── 1 ←── 2 ←── 3 ←── 4
↑
head
- 使用迭代器封装底层节点指针
- 提供
operator*、operator++、operator!=等基本操作 - 符合 STL 迭代器协议,便于与泛型算法(如
sort、find)配合使用
七、总结
本文系统梳理了 C++ STL list 容器的核心设计:
- 迭代器类型 :
list使用双向迭代器,不支持随机访问 - 内部结构:循环双向链表 + 哨兵节点,简化边界处理
- 关键操作 :
empty_initialize初始化哨兵,push_back实现尾部插入 - 迭代器封装:通过封装节点指针,提供符合 STL 规范的迭代器接口
掌握这些原理,有助于深入理解 STL 设计思想,并为手写简易版容器打下坚实基础。
附录:参考资料
- 《C++ Primer》第 10 章:泛型算法与容器
- SGI STL 源码剖析
- cppreference.com:
std::list
任何的批评和建议欢迎指出,我们共同进步!