forward_list 是 C++11 引入的轻量单向链表,仅存后继指针,内存占用低、缓存友好,但仅支持前向遍历、无 size()(部分实现 O(n))、无随机访问;适用于头部频繁增删、内存敏感且无需双向遍历的场景。

forward_list 是什么,为什么不用 list
forward_list 是 C++11 引入的单向链表容器,底层只存后继指针,不存前驱。它比 list 更轻量:每个节点少 8 字节(64 位下),内存占用低、缓存更友好。但代价是只能向前遍历,不支持 size()(C++11 原始版本甚至没有该函数),也不能随机访问或反向迭代。
适合场景:频繁在头部插入/删除、对内存敏感、明确不需要双向遍历或长度查询的场合。比如实现栈式缓冲区、事件队列头插、或作为哈希桶的底层存储。
初始化和基本操作要注意什么
forward_list 构造方式和其他容器类似,但部分操作语义不同——它没有 push_back(),只有 push_front();插入必须通过迭代器(insert_after()),不能像 vector 那样用位置索引。
-
forward_list<int> fl = {1, 2, 3};</int>合法,支持初始化列表 -
fl.insert_after(fl.before_begin(), 0);是头插唯一安全方式,before_begin()返回的是“头节点前”的占位迭代器 -
fl.erase_after(fl.before_begin());删除首元素,别直接用fl.begin()做参数,会越界 - 没有
at()或operator[],想取第 n 个元素得手动std::next(it, n)
为什么 size() 在某些标准库实现里很慢
标准规定 forward_list::size() 可以是 O(1) 或 O(n),取决于实现。libstdc++(GCC)默认不维护长度计数,调用 size() 会遍历整个链表;MSVC 和 libc++ 则多数实现为 O(1)。如果你依赖 size() > 0 做判断,优先用 empty()——它永远是 O(1)。
立即学习“C++免费学习笔记(深入)”;
常见错误现象:if (fl.size()) { ... } 在 GCC 下可能意外拖慢性能,尤其链表很长时。更糟的是,有人误以为 size() 会触发重新计数,其实它只是遍历,不会修改状态。
- 检查是否为空:始终用
fl.empty() - 需要长度且链表不常变:自己缓存一个
size_t count,配合每次push_front()/erase_after()手动更新 - 避免在循环中反复调用
size(),特别是配合std::next查找时
迭代器失效和 erase_after 的坑
forward_list 的迭代器只在被擦除的节点及其后继失效(准确说:指向被删节点的迭代器仍有效,但不可解引用;其后所有迭代器全失效)。这和 vector 或 list 都不同,容易误判。
最典型错误是边遍历边删却没保存 next:
// ❌ 错误:it 在 erase_after 后失效,++it 行为未定义
for (auto it = fl.begin(); it != fl.end(); ++it) {
if (*it == target) fl.erase_after(fl.before_begin(), it);
}
正确做法是用 erase_after 返回的迭代器继续:
// ✅ 正确:erase_after 返回被删节点后的迭代器
auto it = fl.before_begin();
while (next(it) != fl.end()) {
if (*next(it) == target)
it = fl.erase_after(it);
else
++it;
}
-
erase_after(it)删除的是it指向节点的下一个节点,返回新“下一个”节点的迭代器 -
before_begin()是唯一能安全用于头删的迭代器,别试图对begin()调用erase_after() - 插入/删除后,除了被操作影响的那部分,其他迭代器一般保持有效(但别依赖——标准只保证“不指向被删节点”的迭代器有效)











