【问题标题】:What is iterator invalidation?什么是迭代器失效?
【发布时间】:2013-05-30 01:25:23
【问题描述】:

我看到它被引用了很多,但它到底是什么没有明确的答案。我的经验是使用高级语言,所以我不熟悉集合框架中存在的无效性。

什么是迭代器失效?

为什么会出现?为什么难对付?

【问题讨论】:

  • 我可能需要对此进行更好的解释,我认为这与高级/低级语言无关。我知道您无法在 C# 的迭代期间修改列表。
  • @NickFreeman 它与高低水平无关。它与实施有关。完全有可能(尽管很复杂)在 C# 中创建一个允许在枚举时进行迭代的容器,只要您保持每个检查的状态。
  • 嗯,在我使用非 C++ 语言的经验中,我从未听说过迭代器失效,所以我认为它与低级别/性能问题有关,否则所有集合都是相同的。

标签: c++ iterator


【解决方案1】:
  1. 迭代器是美化的指针。迭代器失效很像指针失效。这意味着它突然指向垃圾数据。

  2. 因为这样做是很自然但错误的:

    for(iterator it = map.begin(); it != map.end(); ++it) {
        map.erase(it->first);
        // whoops, now map has been restructured and iterator 
        // still thinks itself is healthy
    }
    
  3. 因为那个错误就在那里?没有编译器错误,没有警告,你输了。你只需要接受足够好的训练来观察它们并阻止它们。如果你不知道自己在做什么,那就是非常隐蔽的错误。 C++ 的设计理念之一是速度胜过安全。在 C++ 语言设计者看来,将导致迭代器失效而不是未指定行为的运行时检查成本太高。

当您迭代数据结构并修改结构本身时,您应该保持高度警惕,而不仅仅是其中包含的对象。那时你可能应该跑到文档并检查操作是否非法。

【讨论】:

  • 这就是为什么我喜欢 Visual Studio 具有调试版本这一事实。默认情况下,标准 C++ 容器将检查它们在调试版本中是否有效,从而帮助发现这些潜在错误。
  • @MooingDuck 你不应该依赖这种行为。如果您过于依赖出色的编译器功能,那么当您转移到不同的平台时(这可能会在未来的某个时间发生),您会被烧死,很难而且很快。
  • @RichardJ.RossIII:依赖?显然不是。但是,当我搞砸的时候,有一层额外的保护是很好的。
  • 但是什么样的迭代器实现能让这种情况发生呢?那不是那种贫民区/不安全吗?
  • @MarkCanlas:这就是 C++ 的工作原理。大多数只能在运行时捕获的错误不需要自动检查;否则,在许多情况下,运行时成本将是不可接受的。所以一个“安全”的实现(正如 Mooing Duck 所描述的)将检查这个错误; “快速”实现不会,而且会快得多。
【解决方案2】:

当使用迭代器处理的容器在处理过程中其形状发生变化时,就会出现此问题。 (我们将假设一个单线程应用程序;对可变容器的并发访问是一个完整的“另一个蠕虫罐头,我们不会在此页面上讨论)。 “改变形状”是指以下类型的突变之一:

  • 插入容器(在任何位置)
  • 删除一个 容器中的元素
  • 任何更改键的操作(在 关联容器)
  • 任何改变顺序的操作 排序容器中的元素。
  • 任何更复杂的操作 由以上一项或多项组成(如拆分容器 一分为二)。

(来自:http://c2.com/cgi/wiki?IteratorInvalidationProblem

这个概念实际上很简单,但副作用可能很烦人。我要补充一点,这个问题不仅会影响 C/C++,还会影响大量其他低级或中级语言。 (在某些情况下,即使它们不允许直接堆分配)

【讨论】:

  • 在我知道的任何关联容器中都无法更改键。
  • @MooingDuck 这真的取决于您认为“关键更改”是什么。我可以删除一个键的值,然后为另一个键添加该值,这不是“键更改”吗?仅仅因为容器本身不支持某些东西,并不意味着它不能完成。
  • @RichardJ.RossIII:我相信帖子/链接指的是键本身的实际修改,而不是与所述键关联的值。在大多数情况下,修改关联数据需要迭代器失效。修改 密钥本身 需要(理论上)擦除/重新插入。
  • @MooingDuck 再次,不一定。如果我的实现是一个键数组和一个值数组,其中值的索引对应于它的键,我可以简单地更改键数组中的值!同样,如果它是一个键值对数组,没有什么说我不能只更改该对的“键”部分。显然没有在 C++ STL 中实现,但也不是不可能。
  • @RichardJ.RossIII:我明白你的意思。我的意思是,这将很难扩展,但这与您的观点无关。我什至可以看到这如何使迭代器无效! O.o
【解决方案3】:

迭代器失效是指迭代器类型(支持运算符++* 的对象)不能正确表示它正在迭代的对象的状态。例如:

int *my_array = new int[15];
int *my_iterator = &my_array[2];

delete[] my_array;

std::for_each(my_iterator, my_iterator + 5, ...); // invalid

这会导致未定义的行为,因为它指向的内存已被操作系统回收。

然而,这只是一种情况,还有许多其他因素会导致迭代器“无效”,您必须仔细检查您正在使用的对象的文档。

【讨论】:

    猜你喜欢
    • 2016-10-26
    • 1970-01-01
    • 2023-01-13
    • 1970-01-01
    • 2014-05-17
    • 2011-01-19
    • 1970-01-01
    • 2020-06-29
    • 2015-07-31
    相关资源
    最近更新 更多