C++ 什么是迭代器无效?

C++ 什么是迭代器无效?,c++,iterator,C++,Iterator,我看到它引用了很多,但没有明确的答案,究竟是什么。我的经验是使用高级语言,因此我不熟悉集合框架中是否存在无效性 什么是迭代器无效 为什么会出现这种情况?为什么很难处理 迭代器是美化的指针。迭代器失效很像指针失效;这意味着它突然指向垃圾数据 因为这样做是很自然的,但却是错误的: for(iterator it = map.begin(); it != map.end(); ++it) { map.erase(it->first); // whoops, now map has

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

什么是迭代器无效

为什么会出现这种情况?为什么很难处理

  • 迭代器是美化的指针。迭代器失效很像指针失效;这意味着它突然指向垃圾数据

  • 因为这样做是很自然的,但却是错误的:

    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
    }
    
  • 因为那个错误就在那里?没有编译器错误,没有警告,您将丢失。你只需要接受足够好的训练来观察和预防它们。如果你不知道自己在做什么,那就是非常阴险的虫子。C++的设计理念之一是安全性的速度。在C++语言设计者看来,运行迭代器检查会导致迭代器失效而不是未指定的行为太昂贵。


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

    迭代器无效是指迭代器类型(支持运算符
    ++
    *
    的对象)不能正确表示其正在迭代的对象的状态时发生的情况。例如:

    int *my_array = new int[15];
    int *my_iterator = &my_array[2];
    
    delete[] my_array;
    
    std::for_each(my_iterator, my_iterator + 5, ...); // invalid
    
    这会导致未定义的行为,因为它指向的内存已被操作系统回收


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

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

    • 插入容器(在任何位置)
    • 删除 容器中的元素
    • 更改密钥的任何操作(在 关联容器)
    • 任何改变操作顺序的操作 已排序容器中的元素
    • 还有更复杂的操作吗 由上述一个或多个组成(如拆分容器 一分为二)
    (发件人:)


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

    此外,我可能需要更好的解释,我认为这与高级/低级语言无关。我知道你不能在
    C.
    @NickFreeman中的迭代过程中修改列表,它与高低级别无关。这一切都与执行有关。在C语言中创建一个容器是完全可能的(虽然很复杂),它可以在枚举时进行迭代,只要你保持每个检查的状态。我在语言的体验中从来没有听说过迭代器失效,而不是C++,所以我认为它与低级别/性能相关的问题有关。否则所有的集合都是一样的。这就是为什么我喜欢VisualStudio有调试版本的事实。默认情况下,标准C++容器将检查它们是否在调试版本中有效,帮助找出这些隐藏的错误。@ MooingDuck,您不应该依赖于这种行为。如果你过于执着于令人敬畏的编译器功能,当你转向另一个平台(这可能在将来某个时候发生)时,你会很快被烧死。@RichardJ.RossIII:依赖于?显然不是。但是当我搞砸的时候,有一个额外的保护层是很好的。但是什么样的迭代器实现可以让这种情况发生呢?这不是贫民窟吗?MarkCanlas:这就是C++的工作原理。大多数只能在运行时捕获的错误不需要自动检查;否则,运行时成本在许多情况下都是不可接受的。所以一个“安全”的实现(正如Mooing Duck所描述的)将检查这个错误;一个“快速”的实现不会,也会快得多。在我知道的任何关联容器中,密钥都不能更改。@ MooingDuck,这取决于你认为的“密钥更改”是什么。我可以删除一个键的值,然后为另一个键添加该值,这不是“键更改”吗?仅仅因为容器本身不支持某些东西,并不意味着它不能完成。@RichardJ.RossIII:我相信帖子/链接指的是对密钥本身的实际修改,而不是与所述密钥相关的值。在大多数情况下,修改关联数据需要迭代器失效。修改密钥本身需要(理论上)擦除/重新插入。@MooingDuck再次,不一定。如果我的实现是一个键数组和一个值数组,其中值的索引对应于它的键,那么我可以简单地更改键数组中的值!类似地,如果它是一个键值对数组,没有任何东西表明我不能只更改该对的“键”部分。显然它没有在C++ STL中实现,但它不是不可能的。@ Rig硬j.Rsisiii:我明白你的意思。我的意思是,这会很糟糕,但这与你的观点无关。我甚至可以看出这会使迭代器失效!O.O