std::list是c++标准库中的双向链表容器,适用于频繁在已知迭代器位置中间增删、需稳定迭代器且无需随机访问的场景;不适用于索引访问或遍历为主的场合。

list 是什么,什么时候该用它
std::list 是 C++ 标准库里的双向链表容器,不是数组、也不是 vector。它不支持随机访问(不能用 [i]),但能在任意位置以 O(1) 时间插入或删除元素——前提是已有迭代器指向那个位置。
常见误用场景:想替代 std::vector 存数据,只因为“听说 list 插入快”。错。如果频繁按索引读取、遍历为主、内存连续性重要(比如和 GPU 或旧 API 交互),std::vector 几乎总是更优。
真正适合 std::list 的情况:
- 频繁在中间增删(比如实现 LRU 缓存时移动节点到头部)
- 不需要随机访问,且插入/删除位置由已有迭代器给出(比如用
find()找到后直接erase(it)) - 需要稳定迭代器:插入/删除不影响其他元素的迭代器有效性(
vector会失效,list不会)
怎么声明、初始化和遍历 list
声明和初始化和其他 STL 容器类似,但注意默认构造不分配节点:
#include <list>
std::list<int> lst; // 空 list
std::list<int> lst2 = {1, 2, 3}; // C++11 初始化列表
std::list<:string> words{"a", "bb", "ccc"};
</:string></int></int></list>
遍历必须用迭代器(不能用下标):
推荐写法(C++11 起):
for (const auto& x : lst) { /* 只读 */ }
for (auto& x : lst) { /* 可修改 */ }
老式写法(需手动管理迭代器):
for (auto it = lst.begin(); it != lst.end(); ++it) {
std::cout <p>⚠️ 常见错误:写成 <code>it —— <code>std::list::iterator</code> 是双向迭代器,不支持 <code> 比较,编译失败。</code></code></p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/shouce/1510" title="C函数速查手册(CHM版)"><img
src="https://img.php.cn/upload/manual/000/000/001/5d6de31fedca2993.png" alt="C函数速查手册(CHM版)" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/shouce/1510" title="C函数速查手册(CHM版)" class="overflowclass">C函数速查手册(CHM版)</a>
<p class="overflowclass">C函数速查手册(CHM版)</p>
</div>
<a rel="nofollow" href="/xiazai/shouce/1510" title="C函数速查手册(CHM版)" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div><h3>插入和删除操作的关键细节</h3><p><code>std::list</code> 提供多个插入接口,但语义差异大:</p>
-
push_back(x)和push_front(x):O(1),最常用 -
insert(it, x):在it之前插入,O(1),但要求it有效(不能是end()以外的非法值) -
emplace_back(args...)/emplace_front(args...):就地构造,避免拷贝(对复杂对象有意义)
删除要注意:
-
erase(it):删单个,返回下一个有效迭代器(C++11 起),可安全用于循环中 -
erase(first, last):删区间,[first, last) -
remove(val):删所有等于val的元素(调用operator==),不是按迭代器删 -
remove_if(pred):更灵活,比如lst.remove_if([](int x) { return x
⚠️ 容易踩坑:
- 用
erase()后继续用原迭代器 → 未定义行为 - 对空
list调用front()或back()→ 崩溃(不抛异常) - 误以为
size()是 O(1):C++11 起是,但某些老标准库实现是 O(n),别依赖它做性能敏感判断
list 和 vector 的实际性能差异在哪
内存布局差异直接决定性能表现:
-
std::list每个元素单独堆分配,含两个指针(前驱/后继),空间开销大(比如int在 64 位系统上占 24 字节:4 字节数据 + 16 字节指针) -
std::vector内存连续,CPU 缓存友好;list节点分散,遍历时 cache miss 高
实测常见场景(10 万元素):
- 顺序遍历:vector 快 3–5 倍
- 尾部插入:两者都 O(1),vector 可能略慢于扩容时,但摊还仍是 O(1)
- 中间插入(已知位置):list 稳定 O(1),vector 是 O(n)
所以,“插入快”只在你已有目标位置的迭代器时成立。如果每次都要 std::find 先找位置,那查找本身是 O(n),插入的 O(1) 就没意义了。
真正影响选择的,往往不是算法复杂度,而是:你是否需要迭代器/引用长期有效?是否频繁在未知位置增删?是否在意内存占用和缓存效率?
list 的接口看似简单,但它的代价藏在内存模型和使用模式里。用错地方,比 vector 慢几倍很常见。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










