C++STL--list底层实现,迭代器分类,模拟list的迭代器封装、实现


博主名称:deM0one ⭐️C++专栏: C++

文章目录

  • [C++ STL list 容器底层原理:从零实现一个类 STL 的 list](#C++ STL list 容器底层原理:从零实现一个类 STL 的 list)
    • 一、引言
    • 二、迭代器类型与性能特性
      • [2.1 迭代器类型](#2.1 迭代器类型)
      • [2.2 性能特点](#2.2 性能特点)
      • [2.3 支持双向迭代器的容器](#2.3 支持双向迭代器的容器)
    • [三、vector 的成员类型详解](#三、vector 的成员类型详解)
    • 四、迭代器分类体系
    • [五、list 的内部结构设计](#五、list 的内部结构设计)
      • [5.1 循环双向链表 + 哨兵节点](#5.1 循环双向链表 + 哨兵节点)
      • [5.2 push_back 实现](#5.2 push_back 实现)
    • 六、迭代器的封装与实现
      • [6.1 普通人的写法(不封装迭代器)](#6.1 普通人的写法(不封装迭代器))
      • [6.2 仿 STL 的迭代器封装](#6.2 仿 STL 的迭代器封装)
    • 七、总结
    • 附录:参考资料

C++ STL list 容器底层原理:从零实现一个类 STL 的 list

一、引言

  • C++ STL(Standard Template Library)是 C++ 标准库的核心组件
  • list 作为双向链表容器,与 vector 等顺序容器有本质区别
  • 本文将从迭代器类型、内部结构、关键操作实现、迭代器封装四个维度,深度解析 list 的工作原理

二、迭代器类型与性能特性

2.1 迭代器类型

类型 说明
iterator 普通迭代器,支持读写
reverse_iterator 反向迭代器,从尾部向前遍历
const_iterator 常量迭代器,只读访问
const_reverse_iterator 常量反向迭代器

2.2 性能特点

  • 支持双向遍历 :可通过 ++-- 操作前后移动
  • 不支持随机访问 :无 operator[],不能通过下标直接访问元素
  • 时间复杂度:
    • 插入/删除:O(1)(已知位置)
    • 查找:O(n)

2.3 支持双向迭代器的容器

  • listmapmultimap
  • setmultiset
  • vectorstringdeque(这些支持随机访问迭代器)

注意:STL 从 C++98 起就支持上述迭代器类型。


三、vector 的成员类型详解

std::vector<T, Alloc> 为例,其成员类型定义如下:

成员类型 定义
value_type 模板第一个参数 T
allocator_type 模板第二个参数 Alloc
reference allocator_type::reference
const_reference allocator_type::const_reference
pointer allocator_type::pointer
const_pointer allocator_type::const_pointer
iterator 指向 value_type 的随机访问迭代器
const_iterator 指向 const value_type 的随机访问迭代器
reverse_iterator reverse<iterator>
size_type 无符号整数类型

关键点vector 的迭代器是随机访问迭代器 ,而 list 的迭代器仅为双向迭代器


四、迭代器分类体系

复制代码
Input ← Output ← Forward ← Bidirectional ← Random Access
迭代器类型 能力说明
Input Iterator 只读、单向、只能前进一次
Forward Iterator 可读写、可多次遍历同一元素
Bidirectional Iterator 可前可后移动(listmap 使用)
Random Access Iterator 支持任意位置跳跃(vectordeque 使用)

五、list 的内部结构设计

5.1 循环双向链表 + 哨兵节点

list 内部采用循环双向链表结构,并设置一个**哨兵节点(sentinel)**作为头节点。

cpp 复制代码
protected:
    link_type node;  // 哨兵节点指针

public:
    list() { empty_initialize(); }

void empty_initialize() {
    node = get_node();      // 分配哨兵节点
    node->next = node;      // 自循环
    node->prev = node;
}

设计意义:哨兵节点简化了边界处理,使得空链表和非空链表的处理逻辑保持一致。

5.2 push_back 实现

cpp 复制代码
void push_back(const T& x) {
    Node* newnode = new Node(x);
    Node* tail = _head->prev;   // 当前尾节点
    tail->next = newnode;       // 原尾部指向新节点
    newnode->prev = tail;       // 新节点指回原尾部
    newnode->next = _head;      // 新节点指向哨兵
    _head->prev = newnode;      // 哨兵指回新节点
}

插入过程图示:

复制代码
         tail        newnode
            ↓           ↓
[head] ←── 1 ←── 2 ←── 3 ←── 4
            ↑           ↑
           head       newnode
  • 在链表尾部插入新节点 newnode
  • 更新原尾节点 tailnext 指向新节点
  • 新节点的 prev 指回 tail
  • 新节点的 next 指向 head
  • headprev 指向新节点 → 形成闭环

六、迭代器的封装与实现

6.1 普通人的写法(不封装迭代器)

cpp 复制代码
typedef list_iterator<T> iterator;

iterator begin() {
    iterator it(_head->next);
    return iterator(_head->next);
}

iterator end() {
    return _head;  // 返回哨兵节点
}

问题:直接暴露底层节点指针,缺乏封装,不符合 STL 规范。

6.2 仿 STL 的迭代器封装

cpp 复制代码
template<class T>
struct list_iterator {
    typedef list_node<T> Node;
    typedef list_iterator<T> Self;
    Node* _node;

    list_iterator(Node* node) : _node(node) {}

    // 解引用操作
    T& operator*() {
        return _node->_data;
    }

    // 前置递增
    Self& operator++() {
        _node = _node->next;
        return *this;
    }

    // 不等比较
    bool operator!=(const Self& s) {
        return _node != s._node;
    }
};

迭代器遍历图示:

复制代码
       pos
        ↓
[head] ←── 1 ←── 2 ←── 3 ←── 4
        ↑
       head
  • 使用迭代器封装底层节点指针
  • 提供 operator*operator++operator!= 等基本操作
  • 符合 STL 迭代器协议,便于与泛型算法(如 sortfind)配合使用

七、总结

本文系统梳理了 C++ STL list 容器的核心设计:

  1. 迭代器类型list 使用双向迭代器,不支持随机访问
  2. 内部结构:循环双向链表 + 哨兵节点,简化边界处理
  3. 关键操作empty_initialize 初始化哨兵,push_back 实现尾部插入
  4. 迭代器封装:通过封装节点指针,提供符合 STL 规范的迭代器接口

掌握这些原理,有助于深入理解 STL 设计思想,并为手写简易版容器打下坚实基础。


附录:参考资料

  • 《C++ Primer》第 10 章:泛型算法与容器
  • SGI STL 源码剖析
  • cppreference.comstd::list

任何的批评和建议欢迎指出,我们共同进步!

相关推荐
彧azz14 小时前
图的存储结构详解:邻接矩阵的原理、实现与应用
开发语言·数据结构·学习·php
IT_Octopus14 小时前
IntelliJ 本地日志路径自定义:`-DLOG_PATH=./logs` 为什么总“不听话“
java·log4j·intellij-idea
hansang_IR14 小时前
【题解】P4460 [CQOI2018] 解锁屏幕
c++·算法
嵌入式学习菌14 小时前
Modbus‑RTU 数据类型分析
开发语言·单片机·bug
长征coder14 小时前
【无标题】
java·性能优化
ChampaignWolf15 小时前
在 SAP S/4HANA 内部基于 ICF Handler 从零实现 ABAP MCP Server:架构、代码与踩坑实录
c++·架构·sap·abap·mcp
matlab代码15 小时前
基于matlab数字图像处理的指纹识别系统【源码68期】
开发语言·matlab
新时代牛马15 小时前
PCI与PCIe 硬件原理、配置空间/BAR 与 Linux 驱动完整篇:从 LTSSM、TLP 到 ECAM 与probe
java·linux·服务器
Bs_MoneyMagnet15 小时前
基于springboot+vue的医疗健康便民服务平台的设计与实现 源码+文档
java·vue.js·spring boot·后端·spring
matlab代码15 小时前
基于matlab的水果识别系统(苹果香蕉菠萝梨桃子)【源码65期】
开发语言·matlab