C++ 迭代器失效

2024 年 8 月 29 日 | 4 分钟阅读

本文将讨论 C++ 中的迭代器失效及其示例。

迭代器失效是 C++ 中用来描述迭代器(一种强大的工具,用于遍历向量、列表或映射等容器)因对容器执行操作而变得无效或无用的情况。这个概念对于理解和生成健壮的 C++ 代码至关重要,尤其是在处理动态数据结构时。

迭代器行为和失效与底层容器及其上执行的操作有着内在的联系。在迭代器失效规则方面,主要有三组包含不同的容器组

  • 无失效:某些容器,例如 std::vector,具有相对稳定的迭代器。除非操作导致内存重新分配,否则指向向量元素的迭代器在大多数操作中保持有效。
  • 特定迭代器失效:像 std::list 和 std::deque 这样的容器可能会在某些操作后使单个迭代器失效。例如,std::list 确保只有指向已删除条目的迭代器才失效,而其他迭代器不受影响。
  • 完全失效:某些容器活动可能会使与之关联的所有迭代器失效。例如,如果 std::vector 的容量在插入元素时超过,则所有先前获取的该向量的迭代器都将失效。

理解这些失效要求对于开发高效且无错误程序至关重要。了解您选择的存储结果的容器类型至关重要。了解在转换过程中发生的事情以及它如何影响代码的其他区域(例如逐步遍历列表)也至关重要

示例

让我们考虑以下示例来说明 C++ 中的迭代器失效

文件名:Iterator.cpp

输出

1 3 14 7 4 

说明

在上面的示例代码中,在遍历向量时添加元素 -1 可能会导致向量的大小超过最大大小。在这种情况下,会为向量分配新的内存,并将所有项目复制到其中。但是,迭代器继续指向旧的内存位置。因此,我们现在可以声明迭代器失效了。这是一种失效类型。

迭代器失效规则

  • 向量:所有指向插入点之前元素的迭代器不受影响,而所有其他迭代器都失效。但是,如果此向量的大小因插入而增加,则如前一个示例所述,所有迭代器都将失效。
  • 双端队列:如果添加的成员位于双端队列的前端或后端,所有迭代器都将失效,但对元素的引用仍然有效。否则,所有迭代器和引用都将失效。
  • 列表:插入/删除:当列表中的元素被擦除或插入时,只有指向已删除/已添加项的迭代器才失效。其他迭代器仍然有效。

迭代器处理原则

  1. 了解容器规则:识别与您交互的各种容器类型(例如列表、映射和向量)关联的独特迭代器失效规则。每个容器都有自己的一套规则,规定何时迭代器因某些操作而失效。
  2. 避免在迭代期间更改容器:避免在使用迭代器遍历容器时更改容器。这包括以可能使迭代器失效的方式添加、删除或调整项目大小。
  3. 重新分配或重建迭代器:在执行可能使迭代器失效的活动后,评估或重建它们以确保其有效性。
  4. 保持迭代器生命周期短:仅在必要时使用迭代器。避免长时间存储迭代器以限制失效的可能性。
  5. 优先使用标准库算法:使用标准库方法(std::find、std::remove_if 等)来生成有效迭代器,而不是手动维护迭代器。在内部,此类方法旨在管理迭代器失效。
  6. 使用迭代器特性:使用 std::iterator_traits 来识别迭代器类别和单个容器关联的失效规则。
  7. 理解重新分配:涉及内存重新分配的操作,例如将向量扩展到其容量以上或将条目放入导致重新散列的映射中,都可能使迭代器失效。
  8. 存储索引而非迭代器:如果需要迭代修改,请考虑使用索引而非迭代器。索引不太可能失效,因为它们表示容器中的位置。
  9. 避免嵌套迭代和容器修改:在使用影响同一容器的嵌套循环时要小心。确保内部循环不会使外部循环中使用的迭代器失效。