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

相关推荐
程序员大辉16 小时前
Win11精简版的天花板:Windows X-Lite 26H1 V3完整安装教程,老电脑也能装
windows·电脑
熊明才17 小时前
PM2 服务器服务运维入门指南
运维·服务器·windows
沉迷学习 日益消瘦17 小时前
(windows环境)白嫖阿里云百炼免费 Token 使用 Claude 教程
windows·阿里云·claude·token·百炼
Mr_Xuhhh17 小时前
深入浅出ArrayList:从线性表到洗牌算法,掌握Java集合核心
windows
sunfdf17 小时前
使用免费工具在 Windows 11/10/8/7 中扩展 C 盘的 3 种方法
windows
月走乂山18 小时前
Trae CLI 全局配置 - Windows PATH 配置
windows
l1t19 小时前
DeepSeek辅助编写的dmp转schema和csv文件c语言程序
c语言·开发语言·windows
qq_4294995719 小时前
更新SD卡文件列表到tablew中的list
windows
雨浓YN19 小时前
OPC DA 通讯开发笔记
windows·笔记
劳埃德福杰20 小时前
Windows系统卸载Edge浏览器
前端·windows·edge