push_back 是安全、线性时间的尾插操作;splice 是零拷贝的元素迁移,但会失效源容器迭代器——两者目的完全不同,不能互相替代。

push_back 是安全、线性时间的尾插操作;splice 是零拷贝的元素迁移,但会失效源容器迭代器——两者目的完全不同,不能互相替代。
push_back 只负责“加一个”,不移动、不复制、不改变其他容器
它只是在当前 list 末尾新建节点并赋值,底层不涉及内存拷贝或节点转移。适用于逐个追加数据的场景,比如读取文件行、接收网络包、构建临时列表。
-
push_back参数必须是与容器value_type兼容的值(支持隐式转换也行),例如mylist.push_back(42)或mylist.push_back(std::move(obj)) - 调用后
size()增加 1,back()返回新插入元素的引用 - 不会影响任何已有迭代器(包括
end()以外的),这是list相比vector的关键优势 - 性能稳定:O(1) 时间复杂度,无重分配、无元素移动
splice 是“剪切粘贴”,不是复制,源容器内容被真实移走
很多人误以为 splice 是高效版的 insert + erase,其实它根本没调用元素的构造/析构函数——只改指针。所以它要求两个容器类型完全一致(std::list<int></int> 和 std::list<int></int>),且不能跨容器类型使用(比如不能从 vector splice 到 list)。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 最常用形式:
dest.splice(dest.end(), src)—— 把整个src搬到dest尾部,src变为空 - 范围迁移:
dest.splice(dest.begin(), src, it1, it2)—— 把src中 [it1,it2) 区间节点移到dest开头 -
src中被 splice 出去的迭代器立即失效;dest中原有迭代器全部保持有效(包括end()) - 如果想把
list最后一个元素挪到开头,写l.splice(l.begin(), l, std::prev(l.end())),别用push_front(pop_back())——后者要析构再构造,开销大得多
常见误用:用 push_back 模拟 splice,或反过来
典型错误是想把另一个 list 的全部内容追加到当前尾部,却写成循环 push_back:
for (auto& x : other_list) {
mylist.push_back(x); // ❌ 多次分配节点,O(n) 次构造
}
这不仅慢,还会触发多次内存分配。正确做法是直接 mylist.splice(mylist.end(), other_list) —— 一次指针调整,0 次构造。
- 反过来,如果只是想加一个字面量(如
5),硬套splice就很荒谬:得先建临时单元素list,再 splice,纯属自找麻烦 -
splice不接受值参数,只接受迭代器或整个容器;push_back不接受迭代器,只接受值或右值 - 当需要保留
other_list原内容时,splice不适用,只能用insert+begin()/end()或循环push_back(此时注意移动语义:用std::move(x)减少拷贝)
容易被忽略的关键细节
splice 的所有重载都要求两个 list 属于同一个内存上下文——不能跨线程安全调用(除非手动加锁),也不能在容器正被另一线程遍历时调用;push_back 虽然线程安全程度略高(单个调用是原子的),但多个线程同时对同一容器 push_back 仍需同步。
- 调试时若发现某迭代器突然解引用崩溃,优先检查是否在它所属容器上执行过
splice(尤其是传入了该迭代器所在容器自身) -
push_back对右值支持良好(C++11 起有void push_back(T&&)重载),但如果你传的是const T&,就不会触发移动,这点在频繁插入大对象时影响明显 -
splice后源容器的size()立即反映变化,但其内部头节点指针不变——这意味着空list的begin() == end()依然成立,不会出现悬空状态
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










