C++STL list实现揭秘

好的,我们来逐步实现一个简化版的 C++ STL list 容器。STL 中的 list 是一个双向链表,它支持高效的插入和删除操作。我们将从底层链表结构开始,逐步封装成一个具有基本功能的容器类。

核心思想:

  1. 双向链表节点: 每个节点包含数据、指向前一个节点的指针和指向后一个节点的指针。
  2. 迭代器: 提供一种访问链表元素的方式,封装节点指针。
  3. 容器封装: 管理链表头尾、大小等状态,提供插入、删除、访问等成员函数。

步骤 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;
}

总结

  1. ListNode: 定义了链表节点的基本结构。
  2. ListIterator: 封装节点指针,提供类似指针的操作(解引用、递增、递减、比较),实现迭代器功能。
  3. List: 管理链表整体结构(哨兵节点 head_, tail_ 简化边界处理),记录大小 size_。提供核心接口:
    • begin(), end(): 获取迭代器。
    • push_back(), pop_back(): 尾部操作。
    • insert(), erase(): 在任意位置插入/删除元素(通过迭代器定位)。
    • size(), empty(): 查询状态。
    • clear(): 清空元素。
  4. 内存管理: 构造函数创建哨兵节点,析构函数释放所有节点(包括哨兵),insertnewerasedelete

这个实现展示了 STL list 的核心思想和基本结构。实际 STL list 实现更复杂,包含常量迭代器 (const_iterator)、反向迭代器 (reverse_iterator)、更丰富的成员函数 (splice, merge, sort, unique 等)、自定义分配器等。但本模拟实现了 list 从底层链表到容器封装的关键步骤。

相关推荐
万邦科技Lafite17 小时前
电商发展新趋势:阿里巴巴商品详情API返回值的深度利用
windows·api·api接口·开放api·电商开放平台·淘宝开放平台
九成宫18 小时前
WSL2 网络优化配置:提升Git克隆与包下载速度
windows·笔记·代理模式·pip·wsl
至善迎风18 小时前
Windows 10/11 如何退出微软账户并改用本地账户登录(含找不到选项的解决方法)
windows·microsoft
风流 少年20 小时前
Python数据类型:类class、反射dataclasses、functools、typing、pydantic
开发语言·windows·python
weixin_4280053020 小时前
C#调用 AI学习从0开始-第1阶段(基础与工具)-第5天完善请求结构
windows·学习·c#·ai请求结构
Jacob程序员20 小时前
Windows下MCP环境配置全攻略
windows
银河外卖员1 天前
VMware 虚拟机安装 Windows 10 系统详细图文教程
windows·vmware
渣渣灰95871 天前
Windows 10 环境部署 Claude Code 教程
windows·vscode·calude code
元Y亨H1 天前
Windows 内置管理员 (Administrator) 账户重命名指南
windows
水饺编程1 天前
第5章,[Win32 章节] :几种典型的颜色
c语言·c++·windows·visual studio