好的,我们来逐步实现一个简化版的 C++ STL list 容器。STL 中的 list 是一个双向链表,它支持高效的插入和删除操作。我们将从底层链表结构开始,逐步封装成一个具有基本功能的容器类。
核心思想:
- 双向链表节点: 每个节点包含数据、指向前一个节点的指针和指向后一个节点的指针。
- 迭代器: 提供一种访问链表元素的方式,封装节点指针。
- 容器封装: 管理链表头尾、大小等状态,提供插入、删除、访问等成员函数。
步骤 1:定义链表节点类 (ListNode)
这是链表的基本构建块。
cpp
template <typename T>
struct ListNode {
T data; // 存储的数据
ListNode<T>* prev; // 指向前一个节点的指针
ListNode<T>* next; // 指向后一个节点的指针
// 构造函数
ListNode(const T& val = T(), ListNode<T>* p = nullptr, ListNode<T>* n = nullptr)
: data(val), prev(p), next(n) {}
};
步骤 2:定义迭代器类 (ListIterator)
迭代器封装了对节点的访问,使其行为类似于指针。
cpp
template <typename T>
class ListIterator {
public:
using iterator_category = std::bidirectional_iterator_tag; // 双向迭代器标签
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using reference = T&;
ListNode<T>* node_ptr_; // 指向当前节点的指针
// 构造函数
ListIterator(ListNode<T>* node) : node_ptr_(node) {}
// 解引用运算符 (获取数据)
reference operator*() const {
return node_ptr_->data;
}
// 成员访问运算符 (->)
pointer operator->() const {
return &(node_ptr_->data);
}
// 前置递增 (++it)
ListIterator& operator++() {
node_ptr_ = node_ptr_->next;
return *this;
}
// 后置递增 (it++)
ListIterator operator++(int) {
ListIterator tmp = *this;
++(*this);
return tmp;
}
// 前置递减 (--it)
ListIterator& operator--() {
node_ptr_ = node_ptr_->prev;
return *this;
}
// 后置递减 (it--)
ListIterator operator--(int) {
ListIterator tmp = *this;
--(*this);
return tmp;
}
// 相等比较
bool operator==(const ListIterator& other) const {
return node_ptr_ == other.node_ptr_;
}
// 不等比较
bool operator!=(const ListIterator& other) const {
return node_ptr_ != other.node_ptr_;
}
};
步骤 3:定义 List 容器类
这个类管理整个链表,包括头尾节点和大小。
cpp
template <typename T>
class List {
private:
ListNode<T>* head_; // 指向哨兵头节点 (不存储有效数据)
ListNode<T>* tail_; // 指向哨兵尾节点 (不存储有效数据)
size_t size_; // 链表元素个数
public:
using iterator = ListIterator<T>; // 定义迭代器类型别名
// 默认构造函数 (构造空链表)
List() : size_(0) {
head_ = new ListNode<T>(); // 创建头哨兵节点
tail_ = new ListNode<T>(); // 创建尾哨兵节点
head_->next = tail_; // 头哨兵指向尾哨兵
tail_->prev = head_; // 尾哨兵指向头哨兵
}
// 析构函数 (释放所有节点内存)
~List() {
clear(); // 清空有效节点
delete head_; // 删除头哨兵
delete tail_; // 删除尾哨兵
}
// 清空链表 (保留哨兵)
void clear() {
while (head_->next != tail_) {
pop_back(); // 不断从尾部删除
}
size_ = 0;
}
// 获取链表大小
size_t size() const {
return size_;
}
// 判断链表是否为空
bool empty() const {
return size_ == 0;
}
// 返回指向第一个有效元素的迭代器
iterator begin() {
return iterator(head_->next);
}
// 返回指向尾哨兵的迭代器 (end 标志位)
iterator end() {
return iterator(tail_);
}
// 在尾部插入一个元素
void push_back(const T& value) {
insert(end(), value); // 在 end() 位置前插入
}
// 删除尾部元素
void pop_back() {
if (empty()) return;
erase(--end()); // 删除 end() 前一个位置的元素
}
// 在指定迭代器位置之前插入一个元素
iterator insert(iterator pos, const T& value) {
ListNode<T>* curr = pos.node_ptr_; // 当前节点 (pos 指向的节点)
ListNode<T>* prev = curr->prev; // 当前节点的前一个节点
ListNode<T>* new_node = new ListNode<T>(value, prev, curr); // 创建新节点
prev->next = new_node; // 前节点指向新节点
curr->prev = new_node; // 当前节点指向新节点
++size_; // 大小增加
return iterator(new_node); // 返回指向新节点的迭代器
}
// 删除指定迭代器位置的元素
iterator erase(iterator pos) {
if (pos == end()) return pos; // 不能删除尾哨兵
ListNode<T>* curr = pos.node_ptr_;
ListNode<T>* prev = curr->prev;
ListNode<T>* next = curr->next;
prev->next = next; // 前节点绕过当前节点
next->prev = prev; // 后节点绕过当前节点
delete curr; // 删除当前节点
--size_; // 大小减少
return iterator(next); // 返回指向下一个元素的迭代器
}
};
简单使用示例
cpp
#include <iostream>
int main() {
List<int> mylist;
// 添加元素
mylist.push_back(10);
mylist.push_back(20);
mylist.push_back(30);
// 遍历打印
for (List<int>::iterator it = mylist.begin(); it != mylist.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl; // 输出: 10 20 30
// 删除第二个元素 (20)
List<int>::iterator it = mylist.begin();
++it; // 指向第二个元素
mylist.erase(it);
// 再次遍历
for (auto num : mylist) { // 如果实现了 const_iterator, 也可以支持范围 for
std::cout << num << " ";
}
std::cout << std::endl; // 输出: 10 30
return 0;
}
总结
ListNode: 定义了链表节点的基本结构。ListIterator: 封装节点指针,提供类似指针的操作(解引用、递增、递减、比较),实现迭代器功能。List: 管理链表整体结构(哨兵节点head_,tail_简化边界处理),记录大小size_。提供核心接口:begin(),end(): 获取迭代器。push_back(),pop_back(): 尾部操作。insert(),erase(): 在任意位置插入/删除元素(通过迭代器定位)。size(),empty(): 查询状态。clear(): 清空元素。
- 内存管理: 构造函数创建哨兵节点,析构函数释放所有节点(包括哨兵),
insert时new,erase时delete。
这个实现展示了 STL list 的核心思想和基本结构。实际 STL list 实现更复杂,包含常量迭代器 (const_iterator)、反向迭代器 (reverse_iterator)、更丰富的成员函数 (splice, merge, sort, unique 等)、自定义分配器等。但本模拟实现了 list 从底层链表到容器封装的关键步骤。