C++ STL list 超详细解析:从接口使用到模拟实现

C++ STL list 超详细解析:从接口使用到模拟实现

本文全面讲解 C++ STL 中 list 容器,包含基础介绍、常用接口、迭代器失效、模拟实现、与 vector 对比,附可直接运行的完整代码,适合学习与面试复习。


一、list 基础介绍

list 是 C++ STL 中的序列式容器 ,底层实现为:
带头结点的双向循环链表

核心特点:

  • 不支持随机访问(不能用 [] 访问)
  • 任意位置插入/删除效率极高 O(1)
  • 插入不会导致迭代器失效,删除仅失效被删节点迭代器

二、list 常用接口使用

1. list 构造函数

构造函数 功能
list() 构造空 list
list(n, val) 构造含 n 个 val 的 list
list(const list& x) 拷贝构造
list(first, last) 用迭代器区间构造

代码示例

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

void test_construct() {
    // 空构造
    list<int> l1;

    // n个val
    list<int> l2(5, 10);

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

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

2. 迭代器(iterator)

list 迭代器是双向迭代器 ,只支持 ++/--,不支持 +n/-n

接口 功能
begin()/end() 正向迭代器
rbegin()/rend() 反向迭代器

代码示例

cpp 复制代码
void test_iterator() {
    list<int> l = {1,2,3,4,5};

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

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

3. 容量与元素访问

接口 功能
empty() 是否为空
size() 元素个数
front() 首元素引用
back() 尾元素引用

代码示例

cpp 复制代码
void test_capacity() {
    list<int> l = {1,2,3};
    cout << "empty: " << boolalpha << l.empty() << endl;
    cout << "size: " << l.size() << endl;
    cout << "front: " << l.front() << endl;
    cout << "back: " << l.back() << endl;
}

4. 插入与删除(核心)

接口 功能
push_back(val) 尾插
pop_back() 尾删
push_front(val) 头插
pop_front() 头删
insert(pos, val) 在 pos 前插入
erase(pos) 删除 pos 位置
swap(list) 交换两个 list
clear() 清空

代码示例

cpp 复制代码
void test_modify() {
    list<int> l;

    // 尾插
    l.push_back(1);
    l.push_back(2);

    // 头插
    l.push_front(0);

    // insert
    auto it = l.begin();
    ++it;
    l.insert(it, 10); // 在 0 后插入 10

    // erase
    it = l.begin();
    l.erase(it);

    // 遍历
    for (auto x : l) cout << x << " ";
    cout << endl;

    l.clear();
}

三、list 迭代器失效问题(高频面试)

核心结论

  • 插入:不会导致任何迭代器失效
  • 删除只有被删除节点的迭代器失效,其他不受影响

错误写法

cpp 复制代码
void TestListIterator1() {
    int arr[] = {1,2,3,4,5};
    list<int> l(arr, arr+5);
    auto it = l.begin();
    while (it != l.end()) {
        l.erase(it); // it 已失效
        ++it;        // 非法访问!
    }
}

正确写法

cpp 复制代码
void TestListIterator() {
    int arr[] = {1,2,3,4,5};
    list<int> l(arr, arr+5);
    auto it = l.begin();
    while (it != l.end()) {
        // 后置++:先传值删除,再自增
        l.erase(it++);
        // 或 it = l.erase(it);
    }
}

四、list 模拟实现(精简完整版)

1. 节点结构

cpp 复制代码
template <class T>
struct ListNode {
    T _data;
    ListNode* _prev;
    ListNode* _next;

    ListNode(const T& val = T())
        : _data(val)
        , _prev(nullptr)
        , _next(nullptr)
    {}
};

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 temp(*this);
        _node = _node->_next;
        return temp;
    }

    // --
    Self& operator--() {
        _node = _node->_prev;
        return *this;
    }
    Self operator--(int) {
        Self temp(*this);
        _node = _node->_prev;
        return temp;
    }

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

3. 反向迭代器(适配器)

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

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

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

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

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;

    // 构造空表
    list() {
        _head = new Node;
        _head->_next = _head;
        _head->_prev = _head;
    }

    // 迭代器
    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()); }

    // 尾插
    void push_back(const T& val) {
        Node* newnode = new Node(val);
        Node* tail = _head->_prev;

        tail->_next = newnode;
        newnode->_prev = tail;
        newnode->_next = _head;
        _head->_prev = newnode;
    }

    // erase
    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);
    }

    // 其他接口:insert、clear、swap、拷贝构造、析构...

private:
    Node* _head;
};

五、list 与 vector 核心对比(面试必背)

对比项 vector list
底层结构 连续动态数组 双向循环链表
随机访问 支持 O(1) 不支持 O(N)
插入删除 需挪动元素 O(N) 不需挪动 O(1)
空间利用率 高、连续、缓存友好 低、易产生碎片
迭代器 原生指针 封装节点指针
迭代器失效 插入/删除易失效 插入不失效,删除仅当前失效
适用场景 频繁访问、少插入 频繁插入/删除、不随机访问

六、总结

  • list双向循环链表,插入删除极快,不支持随机访问。
  • 迭代器是双向迭代器,删除时注意失效问题。
  • 模拟实现核心:节点结构 + 迭代器封装
  • vector 互补,根据场景选择。

相关推荐
Rabitebla11 分钟前
【C++】string 类:原理、踩坑与对象语义
linux·c语言·数据结构·c++·算法·github·学习方法
邪修king18 分钟前
UE5 零基础入门第四弹:UMG UI 系统入门,从静态界面到逻辑联动
c++·ui·ue5
傻啦嘿哟1 小时前
如何在 Python 中使用 colorama 库来给输出添加颜色
开发语言·python
CN-Dust1 小时前
【C++】输入cin例题专题
java·c++·算法
geovindu2 小时前
go: Visitor Pattern
开发语言·设计模式·golang·访问者模式
宣宣猪的小花园.2 小时前
C语言重难点全解析:内存管理到位运算
c语言·开发语言·单片机
方安乐6 小时前
python之向量、向量和、向量点积
开发语言·python·numpy
小小小米粒8 小时前
Collection单列集合、Map(Key - Value)双列集合,多继承实现。
java·开发语言·windows
智者知已应修善业8 小时前
【51单片机中的打飞机设计】2023-8-25
c++·经验分享·笔记·算法·51单片机
czhc11400756639 小时前
C# 428 线程、异步
开发语言·c#