目录
[1. 引言](#1. 引言)
[3. list 类的实现](#3. list 类的实现)
[(1) 基本结构](#(1) 基本结构)
[(2) 初始化与清理](#(2) 初始化与清理)
[(3) 插入与删除](#(3) 插入与删除)
[(4) 常用接口](#(4) 常用接口)
[(4) 常用接口](#(4) 常用接口)
[4. 测试代码](#4. 测试代码)
[5. 总结](#5. 总结)
1. 引言
在C++ STL中,list
是一个基于双向链表 的容器,支持高效的头尾插入/删除操作(O(1)时间复杂度),但不支持随机访问(O(n)时间复杂度)。本文将带你手写一个简化版的 list
,并分析其核心实现。
2. 核心结构:list_node
与 __list_iterator
(1) list_node
:链表的节点
cpp
template<class T>
struct list_node {
list_node<T>* _next; // 指向下一个节点
list_node<T>* _prev; // 指向前一个节点
T _val; // 存储的数据
list_node(const T& val = T())
: _next(nullptr), _prev(nullptr), _val(val)
{}
};
- 双向链表节点 ,包含前驱(
_prev
)、后继(_next
)指针和数据(_val
)。 - 默认构造函数 初始化指针为
nullptr
,数据为默认值T()
(2) __list_iterator
:链表的迭代器
cpp
template<class T, class Ref, class Ptr>
struct __list_iterator {
typedef list_node<T> Node;
typedef __list_iterator<T, Ref, Ptr> self;
Node* _node; // 当前指向的节点
__list_iterator(Node* node) : _node(node) {}
// 解引用(获取数据)
Ref operator*() { return _node->_val; }
// 成员访问(-> 运算符重载)
Ptr operator->() { return &_node->_val; }
// 前置++
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;
}
// 后置--
self operator--(int) {
self tmp(*this);
_node = _node->_prev;
return tmp;
}
// 比较运算符
bool operator!=(const self& it) const { return _node != it._node; }
bool operator==(const self& it) const { return _node == it._node; }
};
- 迭代器核心功能 :
operator*()
:获取当前节点的数据。operator->()
:访问当前节点的成员(如it->_a1
)。++
/--
:支持双向遍历。==
/!=
:判断迭代器是否指向同一节点。
3. list
类的实现
(1) 基本结构
cpp
template<class T>
class list {
typedef list_node<T> Node;
public:
typedef __list_iterator<T, T&, T*> iterator; // 普通迭代器
typedef __list_iterator<T, const T&, const T*> const_iterator; // const迭代器
list() { empty_init(); } // 默认构造
~list() { clear(); delete _head; _head = nullptr; } // 析构
// 拷贝构造
list(const list<T>& lt) {
empty_init();
for (auto& e : lt) push_back(e);
}
// 赋值运算符(现代写法)
list<T>& operator=(list<T> lt) {
swap(lt);
return *this;
}
// 交换两个链表
void swap(list<T>& lt) {
std::swap(_head, lt._head);
std::swap(_size, lt._size);
}
private:
Node* _head; // 哨兵头节点(不存储数据)
size_t _size; // 链表长度
};
- 关键点 :
- 哨兵节点
_head
:简化边界条件处理(begin()
是_head->_next
,end()
是_head
)。 - 拷贝构造:深拷贝,逐个插入元素。
- 赋值运算符 :现代写法(参数传值 +
swap
)。
- 哨兵节点
(2) 初始化与清理
cpp
void empty_init() {
_head = new Node;
_head->_prev = _head;
_head->_next = _head;
_size = 0;
}
void clear() {
iterator it = begin();
while (it != end()) {
it = erase(it);
}
_size = 0;
}
empty_init()
:初始化空链表(哨兵节点自环)。clear()
:逐个删除节点,最后重置_size
。
(3) 插入与删除
cpp
// 在 pos 前插入
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;
++_size;
return iterator(newnode);
}
// 删除 pos 位置的节点
iterator erase(iterator pos) {
assert(pos != end()); // 不能删除哨兵节点
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
prev->_next = next;
next->_prev = prev;
delete cur;
--_size;
return iterator(next);
}
insert
:调整前后指针,插入新节点。erase
:调整指针后删除节点,返回下一个有效迭代器。
(4) 常用接口
cpp
void push_back(const T& x) { insert(end(), x); }
void push_front(const T& x) { insert(begin(), x); }
void pop_back() { erase(--end()); }
void pop_front() { erase(begin()); }
iterator begin() { return iterator(_head->_next); }
iterator end() { return iterator(_head); }
const_iterator begin() const { return const_iterator(_head->_next); }
const_iterator end() const { return const_iterator(_head); }
size_t size() const { return _size; }
(4) 常用接口
cpp
void push_back(const T& x) { insert(end(), x); }
void push_front(const T& x) { insert(begin(), x); }
void pop_back() { erase(--end()); }
void pop_front() { erase(begin()); }
iterator begin() { return iterator(_head->_next); }
iterator end() { return iterator(_head); }
const_iterator begin() const { return const_iterator(_head->_next); }
const_iterator end() const { return const_iterator(_head); }
size_t size() const { return _size; }
4. 测试代码
cpp
void test_list() {
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
for (auto e : lt) cout << e << " "; // 1 2 3
cout << endl;
lt.pop_front();
for (auto e : lt) cout << e << " "; // 2 3
}
5. 总结
list
的核心是双向链表,插入/删除高效(O(1)),但不支持随机访问。- 迭代器是双向迭代器 ,支持
++
/--
,但不支持+
/-
/[]
。 - 哨兵节点简化边界处理 ,
begin()
指向第一个元素,end()
指向哨兵。 - 深拷贝需手动实现(拷贝构造、赋值运算符)。
通过手写 list
,可以更深入理解STL容器的底层实现!