C++ list 模拟实现:带头双向链表的增删查改
前言
相比于 string 和 vector,list 的实现逻辑更为复杂。它没有重载 [] 运算符,也不提供 reserve 扩容接口,但增加了 merge、unique、sort 等特有功能。其核心差异在于迭代器的实现方式。
1. 与 vector/string 的区别
- 无
[]接口:底层非连续存储,随机访问效率低,依赖迭代器。 - 无
reserve接口:每次插入一个节点,无需预分配大块内存。 - 特有接口:如
merge(归并)、unique(去重)、sort(排序)。注意sort底层使用归并排序,因list迭代器不支持减法运算,无法直接使用算法库中的通用排序。 - 迭代器不同:
vector和string使用原生指针作为迭代器,而list必须对迭代器进行包装,重载运算符以满足解引用和自增的核心功能。
一、底层结构与节点构造
list 采用带头双向链表结构。每个节点包含数据域、前驱指针和后继指针。

节点定义:
template <class T>
struct ListNode {
T _data;
ListNode<T>* _prev;
ListNode<T>* _next;
// 构造函数
ListNode(const T& x = T()) : _data(x), _prev(nullptr), _next(nullptr) {}
};
使用 struct 而非 class 是因为成员默认公有,便于在友元函数中直接访问,减少繁琐的 getter/setter。
哨兵位节点:
list 内部维护一个头尾相连的哨兵节点(Sentinel),不存储有效数据。这使得头插、尾插、删除首尾元素的操作逻辑统一,无需特殊判断空表情况。
二、迭代器封装
由于 list 节点不连续,不能直接用裸指针遍历。我们需要封装一个迭代器类。
1. 迭代器分类
迭代器分为普通迭代器和常量迭代器。主要区别在于解引用后返回值的类型是否可写。


