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 从底层链表到容器封装的关键步骤。

相关推荐
三无少女指南44 分钟前
开发者环境配置最佳实践:编辑器Cursor ,VS Code的上位体验实现 AI 与 WSL 联动
运维·c语言·数据库·windows·git·编辑器
水木姚姚1 小时前
string类(C++)
开发语言·c++·windows·vscode·开发工具
love530love2 小时前
ZeroClaw Reflex UI完整搭建流程——ZeroClaw Gateway + LM Studio + Reflex 本地 AI 管理面板
人工智能·windows·gateway·lm studio·reflex·openclaw·zeroclaw
love530love11 小时前
【ComfyUI】解决 ModuleNotFoundError: No module named ‘inference_core_nodes‘ 问题
人工智能·windows·python·comfyui·inference-core
Bruce_Liuxiaowei12 小时前
Windows系统安全加固——从基础到进阶的实战配置
windows·安全·系统安全
RZcKUXIhvPR15 小时前
基于VSG控制(虚拟同步发电机控制)的模块化多电平变流器MM C 通过设置可编程电源,模拟电网...
windows
电饭叔17 小时前
python转换字符串介绍
开发语言·windows·python
winfield82118 小时前
Win11系统,如何让Sublime直接显示在右键点击中?
windows·编辑器·sublime text
winfield82119 小时前
Windows 的 cmd 里如何定义 alias?
windows
小恰学逆向20 小时前
【实用工具趣谈】美化你的终端——你的终端也可以如此炫酷
windows