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

相关推荐
myjie05272 小时前
使用 windows ndk-stack 分析NDK crash
windows
小邓睡不饱耶3 小时前
使用Scala实现手机号码归属地查询系统
开发语言·windows·scala
0白露3 小时前
关闭搜狗输入法右下角广告,可以适用于大多数应用系统通知的广告
windows·bug
欧阳x天4 小时前
STL讲解(七)——list容器的模拟实现
c++·windows·list
嵩山小老虎14 小时前
Windows 10/11 安装 WSL2 并配置 VSCode 开发环境(C 语言 / Linux API 适用)
linux·windows·vscode
AndyHeee17 小时前
【windows使用TensorFlow,GPU无法识别问题汇总,含TensorFlow完整安装过程】
人工智能·windows·tensorflow
远程修电脑→241697677517 小时前
三角洲行动弹窗提示CPU虚拟化未开启或被占用启动腾讯游戏时出现【ACE安全中心】虚拟化相关报错
windows
188_djh17 小时前
# 15_电脑版百度网盘每次登录都显示安全验证,很麻烦,一招解决
windows·app·百度网盘·百度网盘安全验证·baidudisk
郭涤生17 小时前
C++的函数是否可以做到完全覆盖Linux和windows的跨平台
linux·c++·windows