STL list

一、list 介绍

1.1 什么是 list?

listSTL 序列式容器 ,底层是带头结点的双向循环链表

核心特点:

  1. 任意位置插入 / 删除 = O (1) 效率极高
  2. 不支持随机访问 (不能用 []
  3. 可以双向迭代(++-- 都支持)
  4. 插入不会导致迭代器失效
  5. 删除只会让被删节点迭代器失效

与 forward_list 对比:

  • forward_list:单向链表,只能向前,更省空间、更快
  • list:双向链表,更通用、更强大

二、list 的使用(高频接口总结)

1.2.1 list 的构造

cpp 复制代码
#include <iostream>
#include <list>
using namespace std;

void TestList1()
{
    // 1. 空构造
    list<int> l1;

    // 2. 构造 n 个 val
    list<int> l2(5, 10);

    // 3. 拷贝构造
    list<int> l3(l2);

    // 4. 迭代器区间构造
    int arr[] = {1,2,3,4,5};
    list<int> l4(arr, arr+5);
}

1.2.2 迭代器

  • begin() / end():正向迭代器
  • rbegin() / rend():反向迭代器
cpp 复制代码
void TestList2()
{
    list<int> l = {1,2,3,4,5};

    // 正向遍历
    for (auto it = l.begin(); it != l.end(); ++it)
    {
        cout << *it << " ";
    }
    cout << endl;

    // 反向遍历
    for (auto it = l.rbegin(); it != l.rend(); ++it)
    {
        cout << *it << " ";
    }
}

1.2.3 容量操作

cpp 复制代码
l.empty(); // 是否为空
l.size();  // 元素个数

1.2.4 元素访问

cpp 复制代码
l.front(); // 第一个元素
l.back();  // 最后一个元素

1.2.5 修改操作

cpp 复制代码
l.push_back(10);    // 尾插
l.pop_back();       // 尾删
l.push_front(20);   // 头插
l.pop_front();      // 头删

l.insert(it, 30);   // 在 it 前插入
l.erase(it);       // 删除 it 位置
l.clear();          // 清空
l.swap(l2);         // 交换l与l2

三、list 迭代器失效问题

核心结论

  1. list 插入:迭代器不会失效
  2. list 删除:只有被删除节点的迭代器失效

经典错误代码

cpp 复制代码
// 错误
while (it != l.end())
{
    l.erase(it);
    ++it; // 迭代器已失效,野指针!
}

正确写法:

cpp 复制代码
// 正确写法1
while (it != l.end())
{
    l.erase(it++);
}

// 正确写法2
while (it != l.end())
{
    it = l.erase(it);
}

四、list 深度模拟实现(完整版)

4.1 节点结构

cpp 复制代码
template<class T>
struct ListNode
{
    ListNode(const T& val = T())
        :_prev(nullptr)
        ,_next(nullptr)
        ,_data(val)
    {}

private:
    ListNode<T>* _prev;
    ListNode<T>* _next;
    T _data;
};

4.2 正向迭代器

cpp 复制代码
template<class T, class Ref, class Ptr>
struct ListIterator
{
    typedef ListNode<T> Node;
    typedef ListIterator<T, Ref, Ptr> Self;

    Node* _node;

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

    Ref operator*()
    {
        return _node->_data;
    }

    Ptr operator->()
    {
        return &_node->_data;
    }

    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;
    }

    bool operator!=(const Self& it) const
    {
        return _node != it._node;
    }
};

4.3 反向迭代器(适配器)

cpp 复制代码
template<class Iterator>
class ReverseIterator
{
public:
    typedef typename Iterator::Ref Ref;
    typedef ReverseIterator<Iterator> Self;

    ReverseIterator(Iterator it)
        :_it(it)
    {}

    Ref operator*()
    {
        Iterator temp(_it);
        --temp;
        return *temp;
    }

    Self& operator++()
    {
        --_it;
        return *this;
    }

    bool operator!=(const Self& s) const
    {
        return _it != s._it;
    }

private:
    Iterator _it;
};

4.4 list 主体

cpp 复制代码
template<class T>
class list
{
    typedef ListNode<T> Node;
public:
    typedef ListIterator<T, T&, T*> iterator;
    typedef ListIterator<T, const T&, const T*> const_iterator;
    typedef ReverseIterator<iterator> reverse_iterator;

    iterator begin() { return iterator(_head->_next); }
    iterator end() { return iterator(_head); }

    reverse_iterator rbegin() { return reverse_iterator(end()); }
    reverse_iterator rend() { return reverse_iterator(begin()); }

    list()
    {
        _head = new Node;
        _head->_next = _head;
        _head->_prev = _head;
    }

    void push_back(const T& x)
    {
        insert(end(), x);
    }

    iterator insert(iterator pos, const T& x)
    {
        Node* cur = pos._node;
        Node* prev = cur->_prev;
        Node* newnode = new Node(x);

        prev->_next = newnode;
        newnode->_prev = prev;
        newnode->_next = cur;
        cur->_prev = newnode;

        return iterator(newnode);
    }

    iterator erase(iterator pos)
    {
        Node* cur = pos._node;
        Node* prev = cur->_prev;
        Node* next = cur->_next;

        prev->_next = next;
        next->_prev = prev;
        delete cur;

        return iterator(next);
    }

    ~list()
    {
        clear();
        delete _head;
        _head = nullptr;
    }

    void clear()
    {
        iterator it = begin();
        while (it != end())
        {
            it = erase(it);
        }
    }

private:
    Node* _head;
};

五、list 与 vector 对比

表格

维度 vector list
底层结构 连续数组 双向循环链表
随机访问 支持 O (1) 不支持
插入删除 O(N) O(1)
空间利用率
迭代器 原生指针 封装迭代器
迭代器失效 容易失效 插入不失效
适用场景 随机访问、少插入 频繁插入删除

六、list 工程实际使用场景

list 在实际开发中非常常用,主要用于:

  1. 需要频繁在任意位置插入 / 删除

    • 消息队列
    • 任务调度链表
    • 撤销 / 重做栈
    • 游戏对象管理列表
  2. 对插入性能要求极高

    • 后端事件链表
    • 内存池管理
    • LRU 缓存结构
  3. 不需要随机访问

    • 日志管理
    • 异步任务队列

一句话总结:需要频繁插入删除,不关心随机访问 → 用 list


七、总结

  1. list = 带头双向循环链表
  2. 插入删除 O (1),不支持随机访问
  3. 迭代器是封装类型,不是原生指针
  4. 插入不失效,删除只失效当前迭代器
  5. 模拟实现核心:节点 + 迭代器封装 + 反向迭代器适配器
  6. 工程中用于频繁插入删除的队列、链表、任务系统
相关推荐
玖釉-2 小时前
深入解析 meshoptimizer:基于 meshopt_spatialClusterPoints 的空间聚类与 Mesh Shader 前置优化
c++·windows·图形渲染·聚类
xyq20242 小时前
R 绘图 - 函数曲线图
开发语言
wenhaoran112 小时前
CF1800F Dasha and Nightmares
c++·算法·字符串·codeforces·位运算
极客智造2 小时前
深入理解 C++ 友元机制:语法、特性与工程实践
c++
Lyyaoo.2 小时前
【JAVA基础面经】进程间的通信方式
java·开发语言·python
郭涤生2 小时前
C++ 标准库中性能较高的函数总结复习
c++
小此方2 小时前
Re:思考·重建·记录 现代C++ C++11篇 (三) 深度解构:可变参数模板、类功能演进与 STL 的新版图
开发语言·c++·stl·c++11·现代c++
小坏讲微服务2 小时前
Claude Code 终极实战指南:从终端 Agent 到 AI+Java 开发
java·开发语言·人工智能
爱学习的小囧2 小时前
ESXi 8.0 vSwitch与dvSwitch(分布式交换机)核心区别
服务器·开发语言·分布式·php·虚拟化