C++ list splice用法 C++ 链表节点接合操作演示【操作】

7次阅读

splice 是节点移动而非复制,不调用构造/析构函数指针仍有效;仅限同类型同分配器 list 间操作;三种重载分别移动整个容器、单个节点或范围;目标迭代器不失效,源中被移节点的迭代器仍有效但不可用于原容器操作。

C++ list splice用法 C++ 链表节点接合操作演示【操作】

splice 会移动节点,不是复制

splice 的核心行为是“剪切粘贴”:它把源 list 中的节点直接挪到目标 list 的指定位置,不调用任何元素的构造或析构函数。这意味着如果原 list 中有指针指向某个节点,splice 后这些指针仍然有效——因为节点内存地址没变,只是在链表中的前后关系变了。

常见误用是以为 splice 会深拷贝数据,结果发现源 list 被清空(或部分清空),而目标 list 多了节点,但对象本身没重建。这在管理资源(如文件句柄、动态分配内存)时尤其危险——若节点内含非 trivial 类型且依赖析构清理,而你误删了源容器却忘了析构,就可能泄漏。

  • 只适用于同类型 std::list 之间操作,不能跨类型(比如 list 拼到 list
  • 被 splice 的节点所属的 list 必须和当前 list 是同一个模板实例(同一 T,同一分配器)
  • 若使用带迭代器范围的重载(如 splice(pos, other, first, last)),firstlast 必须来自 other,且 first 可达 lastlast 可为 other.end()

三种 splice 调用方式的区别

标准库提供三个主要重载,参数语义差异直接影响行为边界:

  • splice(pos, other):把整个 other 搬到 pos 前面;other 变为空
  • splice(pos, other, it):把 other 中迭代器 it 指向的单个节点移到 pos 前面;it 必须有效且不能等于 other.end()
  • splice(pos, other, first, last):把 [first, last) 范围内的节点(左闭右开)整体移入;firstlast 都必须属于 other,且 first != last 才真正移动(否则无操作)

注意:pos 是目标容器的插入位置,始终解释为“在 pos 之前插入”。例如 lst.splice(lst.begin(), other) 表示插到最前面;lst.splice(lst.end(), other) 等价于追加。

立即学习C++免费学习笔记(深入)”;

迭代器失效规则:只有被移动的迭代器还有效

splice 是少数几个不使**目标容器**迭代器失效的标准容器操作之一。但要注意:源容器中被移动节点对应的迭代器,在移动后依然有效(仍指向原节点),只是不再属于原容器;而源容器中未被移动的其他迭代器,也全部保持有效——因为 list 的节点不重排,仅调整指针。

容易踩的坑:

  • 对已 splice 出去的迭代器继续调用 ++it 或解引用,没问题;但它再也不能用于原 other 容器的 erase/insert/splice 等操作
  • 若在 splice 后还用 other.begin() 等获取新首节点,要小心——other 可能已为空,other.begin() == other.end()
  • 不要在 splice 过程中修改参与操作的任意迭代器变量,比如:auto it = other.begin(); splice(pos, other, it); ++it; —— 此时 it 已不属于 other,但自增行为未定义(虽常能跑通,属未定义行为)

实际拼接场景:避免重复遍历

比如要把两个有序 list 合并成一个有序链表,别先 mergesplice,而应直接用 list::merge——它内部就是基于 splice 实现的高效归并。但如果你只是想把一截中间段“抽出来”插到另一处,splice 就是唯一低开销方案。

示例:从 src 中取出第 2 到第 4 个节点(0-indexed),插入到 dst 开头:

auto it1 = std::next(src.begin(), 1); // 第2个 auto it2 = std::next(src.begin(), 4); // 第5个(作为 last) dst.splice(dst.begin(), src, it1, it2);

这里没用 std::advance 是因为 list 迭代器是双向的,std::next 更安全;it2 必须是“末尾之后”的位置,否则范围不合法。

真正复杂的地方在于:当你要 splice 的范围依赖运行时条件(比如按值筛选),就必须先遍历找边界,而 list 不支持 O(1) 随机访问——这时候 splice 的优势会被抵消,不如考虑换用 vector 或预建索引。

text=ZqhQzanResources