【问题标题】:About invalidating iterators关于使迭代器无效
【发布时间】:2011-08-28 05:01:18
【问题描述】:

STL 容器存在迭代器在容器更改时可能失效的问题。容器是否有可能通过添加调用 has_changed() 来宣布它已更改?

在某些操作之前查询 empty() 是很常见的。如果容器在会影响诸如 insert() 或 erase() 之类的迭代器的操作上设置布尔值,则可以在重用迭代器之前查询 has_changed() 。

疯了吗?

编辑感谢您的大量回复和深思熟虑。我希望我能奖励不止一位获奖者。

【问题讨论】:

  • 如果你有一个Container::has_changed() 方法,你会怎么做?你会对你的迭代器做什么?
  • 实际上你可能想要bool Container::is_still_valid(Container::iterator)。这既解决了“改变以来?”和“并非所有迭代器都因每次更改而失效”的反对意见。 (我称它为 is_still_valid 是因为如果将其限制为过去对该容器有效的迭代器,则实现会容易得多)

标签: c++


【解决方案1】:

有点疯狂,如果是出于好意的话。

我认为主要问题是容器如何知道它何时“未更改”?换句话说,某些东西被擦除并设置了“已更改”标志。因为它回到正常或稳定状态而重置标志的诱发事件是什么?实际上是迭代器而不是容器处于无效状态。

我认为要让它在所有迭代器上工作,它们需要比现在更智能,并且更像容器的观察者。容器可以向已注册的迭代器发送一个它已更改的事件,并且它们可以在尝试操作之前检查自己的状态。但即使这样也有很多漏洞,它可能会导致比你试图解决的问题更大的混乱。

【讨论】:

  • 同意答案,一些实现确实添加了对更改检测的支持,特别是 VS 附带的 Dinkumware 的实现可以使用检查迭代器进行编译,该迭代器将检测(并中止)容器是否已更改。我相信实际的实现在 channel9 视频中进行了解释。请注意,检查的迭代器比未检查的迭代器慢得多。性能影响(内存和 CPU)是不可忽略的——好吧,如果你没有广泛使用迭代器...
【解决方案2】:

这将是低效的,因为容器需要 A) 提供另一个数据字段并 B) 相应地更新它。然而,STL 被认为是为了尝试做不可能的事情并将效率与抽象结合起来。 (它成功了,我应该补充一下。)

如果您需要将引用保存到不断变化的容器中,请使用不会使迭代器无效的容器(std::liststd::set)或将索引保存到std::vector

【讨论】:

    【解决方案3】:

    快速失败迭代器在 Java 中的(近似)工作方式是容器每次更改时都会增加一个计数器。迭代器在创建时复制此计数器,并在每次通过它们更改容器时将其递增。如果迭代器检测到不匹配,则抛出异常。

    C++ 具有令人兴奋的特性,即某些操作使容器上的 some 迭代器无效,但其他操作无效。例如,假设已保留足够的空间vector::insert 在插入点之后而不是之前使迭代器无效。

    另一个困难的案例是list::remove。这使得所有迭代器都有效,除了被移除的迭代器,和它的所有副本

    正如您想象的那样,要精确跟踪这一点非常困难。在实践中发生的情况是,您的实现可能会提供调试选项,迭代器将尽最大努力检测它们是否有效。不过,这可能取决于它们当前是否“工作”的实施细节,而不是标准是否保证它们仍然工作。

    在 C++ 中可以做一些类似于 Java 中的“版本号”的事情,但它会给出看似无效但实际上有效的迭代器的误报。

    【讨论】:

      【解决方案4】:

      迭代器何时失效的规则非常明确,如果您按规则付费,您不应该需要询问容器何时其容器失效。

      此外,迭代器并不总是同时失效。从向量中间删除一个元素只会使该元素及其后面的元素无效。但是指向向量开头的迭代器仍然有效。

      在列表中,迭代器通常只有在它们指向的特定节点被销毁时才会失效。

      所以你不能询问容器你的迭代器是否有效。

      常见的标准库实现可以选择启用与您请求的类似的额外运行时检查,但实现更复杂(必须如此,才能正确),并且会损害性能。

      【讨论】:

      • 我不确定我是否同意“迭代器失效的规则是否足够清楚”。似乎有很多规则。当您考虑它们为什么是规则时,它们是有道理的,但是当操作将失效和迭代器(以及哪些迭代器可能会失效)时,仍然不总是清楚或明显的。正如您接下来的两点所示...
      • @Micheal:规则也不是特别具体。添加到向量可能会使所有迭代器失效。 (当然,我可以检查它的容量与大小,但这正是 OP 所要求的。)
      • 没错,但有一个明确的权衡。您制作的迭代器越智能和“安全”,您获得的性能就越差,它们的通用性越差(祝您好运,让指针表现得像那样),并且它们将越难实现(并正确实现) )。规则有时只会告诉您“您的迭代器可能无效”,但它们足够清楚,可以使用它们
      【解决方案5】:

      这在技术上是可行的,并且 MSVC 实现了提供类似功能的“检查迭代器”(http://msdn.microsoft.com/en-us/library/aa985965.aspx)。虽然当迭代器无效时您不会收到通知,并且您无法直接查询状态(据我所知),但会引发异常和/或调用 invalid_parameter,具体取决于构建的配置方式。

      但是,它显然是非标准的,并且会显着影响性能。对调试很有用。

      【讨论】:

        【解决方案6】:

        容器是否可以通过添加调用 has_changed() 来宣布它已更改?

        我认为,是的,它可以实现。这是一个非常简单的尝试(不是那么优雅,但仍然):

        bool has_changed(std::vector<int> &v)
        {
            static std::map<void*, size_t> has_changed_info;
            void *ptr = &v;
            std::map<void*, size_t>::iterator it = has_changed_info.find(ptr);
            if (it == has_changed_info.end())
            {
               has_changed_info[ptr] = v.capacity();  
               return false;
            }
            if ( it->second != v.capacity() )
            {
                it->second = v.capacity();
                return true;
            }
            return false;
        }
        

        测试代码为:

        int main() {
                std::vector<int> v;
                has_changed(v);
                for ( int i = 0 ; i < 100 ; ++i )
                {
                    v.push_back(i);
                    if (has_changed(v))
                          cout << "changed at " << i << endl;
                }       
                return 0;
        }
        

        输出(GCC-4.3.4):

        changed when inserting i = 0
        changed when inserting i = 1
        changed when inserting i = 2
        changed when inserting i = 4
        changed when inserting i = 8
        changed when inserting i = 16
        changed when inserting i = 32
        changed when inserting i = 64
        

        在线演示:http://www.ideone.com/QmO5q

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2022-12-31
          • 2021-10-16
          • 1970-01-01
          • 2011-05-06
          • 1970-01-01
          • 2021-09-18
          • 2012-05-09
          相关资源
          最近更新 更多