跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

深入理解 C++ STL list 底层实现与模拟实现

C++ STL list 基于带头双向循环链表实现,支持常数时间插入删除及双向迭代。介绍其构造、迭代器使用、容量与数据访问函数,分析迭代器失效机制,对比 list 内部 sort 与算法库 sort 的区别,并提供正向与反向迭代器的模拟实现代码,最后对比 list 与 vector 的差异及适用场景。

剑仙发布于 2026/3/24更新于 2026/7/620K 浏览
深入理解 C++ STL list 底层实现与模拟实现

1. list 的介绍

① STL::list 的底层实现是带头双向循环链表结构。

② list 是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。

③ 双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。

2. list 的使用

2.1 list 的构造

构造函数接口说明
list(size_t n , const T& val = T())构造的 list 中包含 n 个值为 val 的元素
list()构造空的 list
list(const list& x)拷贝构造函数
list(InputIterator first ,InputIterator last)用 [first, last) 区间中的元素构造 list
int main() { 
    list<int> lt1; // 构造空的 lt1 
    list<int> lt2(4, 100); // lt2 中放 4 个值为 100 的元素 
    list<int> lt3(lt2.begin(), lt2.end()); // 用 lt2 的 [begin(), end())左闭右开的区间构造 lt3 
    list<int> lt4(lt3); // 用 lt3 拷贝构造 lt4 
    return 0; 
}

2.2 list iterator 的使用

注意:list 的迭代器的底层是链表的节点,其访问链表中的数据,是通过操作符重载的方式来进行实现的。

函数声明接口说明
begin + end返回第一个元素的迭代器 + 返回最后一个元素的下一个位置的迭代器
rbegin + rend返回第一个元素的 reverse_iterator 即 end 位置 + 返回最后一个元素的下一个位置的 reverse_iterator,即 begin 位置

文章配图

2.3 list 关于容量的函数

函数声明接口说明
empty()检测 list 是否为空,是返回 true,不是则返回 false。
size()返回 list 有效节点的个数。
#include<iostream>
#include<list>
using namespace std;
int main() {
    list<int> lt;
    lt.push_back(1);
    lt.push_back(2);
    lt.push_back(3);
    cout << "size:" << lt.size() << endl;
    cout << "empty:" << lt.empty() << endl;
    return 0;
}

2.4 list 中的数据访问函数

函数声明接口说明
front返回 list 的第一个节点中的引用
back返回 list 的最后一个节点中的值的引用

2.5 list 关于数据的函数

函数声明接口说明
push_front在 list 首元素前插入值为 val 的元素
pop_front删除 list 中的第一个元素
push_back在 list 尾部插入值为 val 的元素
pop_back删除 list 中最后一个元素
insert在 list 的 pos 位置中插入值为 val 的元素
erase删除 list 中 pos 位置的元素
swap交换两个 list 中的元素
clear清空 list 中的有效元素
// list 插入和删除
// push_back/pop_back/push_front/pop_front
void TestList3() {
    int array[] = { 1, 2, 3 };
    list<int> L(array, array + sizeof(array) / sizeof(array[0]));
    // 在 list 的尾部插入 4,头部插入 0
    L.push_back(4);
    L.push_front(0);
    PrintList(L);
    // 删除 list 尾部节点和头部节点
    L.pop_back();
    L.pop_front();
    PrintList(L);
}

// insert /erase
void TestList4() {
    int array1[] = { 1, 2, 3 };
    list<int> L(array1, array1 + sizeof(array1) / sizeof(array1[0]));
    // 获取链表中第二个节点
    auto pos = ++L.begin();
    cout << *pos << endl;
    // 在 pos 前插入值为 4 的元素
    L.insert(pos, 4);
    PrintList(L);
    // 在 pos 前插入 5 个值为 5 的元素
    L.insert(pos, 5, 5);
    PrintList(L);
    // 在 pos 前插入 [v.begin(), v.end) 区间中的元素
    vector<int> v{ 7, 8, 9 };
    L.insert(pos, v.begin(), v.end());
    PrintList(L);
    // 删除 pos 位置上的元素
    L.erase(pos);
    PrintList(L);
    // 删除 list 中 [begin, end) 区间中的元素,即删除 list 中的所有元素
    L.erase(L.begin(), L.end());
    PrintList(L);
}

// resize/swap/clear
void TestList5() {
    // 用数组来构造 list
    int array1[] = { 1, 2, 3 };
    list<int> l1(array1, array1 + sizeof(array1) / sizeof(array1[0]));
    PrintList(l1);
    // 交换 l1 和 l2 中的元素
    list<int> l2;
    l1.swap(l2);
    PrintList(l1);
    PrintList(l2);
    // 将 l2 中的元素清空
    l2.clear();
    cout << l2.size() << endl;
}

2.6 list 迭代器失效

迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list 的底层结构为带头结点的双向循环链表,因此在 list 中进行插入时是不会导致 list 的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。

示例:

void TestListIterator1() {
    int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    list<int> lt(array, array+sizeof(array)/sizeof(array[0]));
    auto it = lt.begin();
    while (it != lt.end()) {
        // erase() 函数执行后,it 所指向的节点已被删除,
        //因此 it 无效,在下一次使用 it 时,必须先给其赋值
        lt.erase(it);
        ++it;
    }
}

// 改正
void TestListIterator() {
    int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    list<int> lt(array, array+sizeof(array)/sizeof(array[0]));
    auto it = lt.begin();
    while (it != lt.end()) {
        lt.erase(it++); // it = l.erase(it);
    }
}

3. list 中的 sort 和算法库中的区别

list 中的 sort 底层实现是归并排序实现的。(时间复杂度高,效率低)

算法库中的 sort 底层是快排实现的。(时间复杂度低,效率高)

文章配图

效率测试和对比:

文章配图

4. list 的模拟实现及正向和反向迭代器解析

4.1 list 的迭代器封装

list 的迭代器和 string 和 vector 迭代器的区别: 首先我们得清楚,list 中每一个节点,在空间都是非连续存储的,不像 string 和 vector 的迭代器一样直接对迭代器++/--即可,由于其底层是内置类型的指针形似而它们在内存中存储数据又是在连续的空间上存储的,不需要我们进行因此我们在对迭代器做 ++/ -- 等操作的时候重载运算符。

我们应该怎么办呢?

通过封装迭代器,对++、--等运算符实现重载。

迭代器的成员变量的解析:

文章配图

4.2 迭代器模板参数的解析

文章配图

4.3 正向迭代器模拟实现

template<class T> struct list_node {
    list_node<T>* _next=nullptr;
    list_node<T>* _prev=nullptr;
    T _val;
    list_node(const T& val = T()) :_val(val) {}
};

template<class T,class Ref,class Ptr> struct __list_iterator {
    typedef list_node<T> Node;
    typedef __list_iterator<T, Ref,Ptr> Self;
    Node* _node;
    __list_iterator(Node* node) :_node(node) {}
    Ref operator*() { return _node->_val; }
    Ptr operator->() { return &_node->_val; }
    Self& operator++() { _node = _node->_next; return *this; }
    Self operator++(int) { Self tmp(*this); _node = _node->_next; return tmp; }
    Self& operator--() { _node = _node->_prev; return *this; }
    Self operator--(int) { Self tmp(*this); _node = _node->_prev; return tmp; }
    bool operator!=(const Self& it) const { return _node != it._node; }
    bool operator==(const Self& it) const { return _node == it._node; }
};

4.4 反向迭代器模拟实现

原理和正向迭代器差不多,反向迭代器的成员变量的类型是正向迭代器,通过操作符重载的方式实现逻辑变化,反向迭代器的操作符重载实现,底层原理都是去调用正向迭代器的操作符重载,唯一区别就是逻辑不同。

template<class iterator,class Ref,class Ptr> struct Reverse_iterator {
public:
    typedef Reverse_iterator<iterator, Ref, Ptr> Self;
    iterator _it;
    Reverse_iterator(iterator it) :_it(it) { }
    Self operator++() { --_it; return *this; }
    Self operator--() { ++_it; return *this; }
    Ref operator*() { iterator tmp = _it; return *(--tmp); }
    Ptr operator->() { return &(operator*()); }
    bool operator!=(const Self& s) const { return _it != s._it; }
};

4.5 list 函数模拟实现

template<class T> struct list_node {
    list_node<T>* _next=nullptr;
    list_node<T>* _prev=nullptr;
    T _val;
    list_node(const T& val = T()) :_val(val) {}
};

template<class T> class list {
    typedef list_node<T> Node;
public:
    typedef __list_iterator<T,T&,T*> iterator;
    typedef __list_iterator<T, const T&,const T*> const_iterator;
    typedef Reverse_iterator<iterator, T&, T*> reverse_iterator;
    typedef Reverse_iterator<const_iterator, const T&,const T*> reverse_const_iterator;
    //反向迭代器
    reverse_iterator rbegin() { return reverse_iterator(_head); }
    reverse_iterator rend() { return reverse_iterator(_head->_next); }
    //const 反向迭代器
    reverse_const_iterator rbegin() const { return reverse_const_iterator(_head); }
    reverse_const_iterator rend() const { return reverse_const_iterator(_head->_next); }
    //正向迭代器
    iterator begin() { return iterator(_head->_next); }
    iterator end() { return _head; }
    //const 正向迭代器
    const_iterator begin() const { return const_iterator(_head->_next); }
    const_iterator end() const { return const_iterator(_head); }
    void empty_init() {
        _head = new Node;
        _head->_prev = _head;
        _head->_next = _head;
        _sz = 0;
    }
    list() { empty_init(); }
    list(const list<T>& lt) {
        empty_init();
        auto it = lt.begin();
        while (it != lt.end()) {
            push_back(*it);
            ++it;
        }
    }
    void swap(list<T>& lt) {
        std::swap(_head, lt._head);
        std::swap(_sz, lt._sz);
    }
    list<T>& operator=(list<T> lt) {
        swap(lt);
        return *this;
    }
    void push_back(const T& x) { insert(end(), x); }
    void push_front(const T& x) { insert(begin(), x); }
    void pop_back() { erase(--end()); }
    void pop_front() { erase(begin()); }
    //pos 之前插入
    iterator insert(iterator pos, const T& x) {
        Node* cur = pos._node;
        Node* prev = cur->_prev;
        Node* newnode = new Node(x);
        cur->_prev = newnode;
        newnode->_prev = prev;
        prev->_next = newnode;
        newnode->_next = cur;
        ++_sz;
        return newnode;
    }
    iterator erase(iterator pos) {
        assert(pos != end());
        Node* cur = pos._node;
        Node* prev = cur->_prev;
        Node* next = cur->_next;
        prev->_next = next;
        next->_prev = prev;
        delete cur;
        --_sz;
        return next;
    }
    size_t size() { return _sz; }
    void clear() {
        list<T>::iterator it = begin();
        while (it != end()) {
            it = erase(it);
        }
        _sz = 0;
    }
    ~list() { clear(); delete _head; _head = nullptr; }
private:
    Node* _head;
    size_t _sz; //用于记录 list 中有效数据的个数
};

5. list 和 vector 的对比

vectorlist
底层结构动态顺序链表,一段连续空间带头结点的双向循环链表
随机访问支持随机访问,访问某个元素效率 O(1)不支持随机访问,访问某个元素效率 O(N)
插入和删除任意位置插入和删除效率低,需要搬移元素,时间复杂度为 O(N),插入时有可能需要增容,增容:开辟新空间,拷贝元素,释放旧空间,导致效率更低任意位置插入和删除效率高,不需要搬移元素,时间复杂度为 O(1)
空间利用率底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高底层节点动态开辟,小节点容易造成内存碎片,空间利用率低,缓存利用率低
迭代器原生态指针对原生态指针 (节点指针) 进行封装
迭代器失效在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效,删除时,当前迭代器需要重新赋值否则会失效插入元素不会导致迭代器失效,删除元素时,只会导致当前迭代器失效,其他迭代器不受影响
使用场景需要高效存储,支持随机访问,不关心插入删除效率大量插入和删除操作,不关心随机访问
}

目录

  1. 1. list 的介绍
  2. 2. list 的使用
  3. 2.1 list 的构造
  4. 2.2 list iterator 的使用
  5. 2.3 list 关于容量的函数
  6. 2.4 list 中的数据访问函数
  7. 2.5 list 关于数据的函数
  8. 2.6 list 迭代器失效
  9. 3. list 中的 sort 和算法库中的区别
  10. 4. list 的模拟实现及正向和反向迭代器解析
  11. 4.1 list 的迭代器封装
  12. 4.2 迭代器模板参数的解析
  13. 4.3 正向迭代器模拟实现
  14. 4.4 反向迭代器模拟实现
  15. 4.5 list 函数模拟实现
  16. 5. list 和 vector 的对比
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 从零构建大模型:深入理解大型语言模型原理与实现
  • 前端 Canvas 基础与实战应用
  • 高中学历转行网络安全工程师:四个月职业逆袭之路
  • 大模型安全攻防:时间诱导越狱与对齐技术深度解析
  • AI 赋能原则 10:人人获益推动生产力新时代
  • RAG 检索增强生成技术要点及应用实践
  • Open WebUI 新技术 MCPo:将 MCP 工具转换为 OpenAPI 接口并支持 Ollama
  • 归并排序时间复杂度 O(nlogn) 解析:排序链表实现
  • 主成分分析(PCA)原理与实战应用
  • 网络基础安全六大组件:防火墙、WAF、IPS、行为管控、DDoS 及蜜罐详解
  • 初学 C++ 必须掌握的核心知识点
  • 低代码平台在人事管理中的应用方案
  • 6本AI领域名家名作推荐,大模型时代学习指南
  • CentOS 部署 Teemii 搭建私人漫画阅读库
  • 设计支持万人并发抢购的秒杀系统架构方案
  • 大模型参数有效微调(PEFT)技术综述
  • Dart 标准 web_socket 库跨平台 WebSocket 实现解析
  • DeepSeek 使用指南与高阶提示词技巧
  • Python FastAPI 入门实战:从零构建生产级 RESTful API
  • C++ 继承机制详解:概念、访问权限与默认成员函数

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online