【问题标题】:Efficient way to iterate a std::list that's changing?迭代正在变化的 std::list 的有效方法?
【发布时间】:2013-06-06 16:56:58
【问题描述】:

我正在尝试遍历 std::list,但有一个问题 - 迭代期间执行的操作最终可能会在列表中添加或删除元素。在这种情况下,添加不是问题,但删除可能会导致列表中的任何迭代器失效,包括序列中的当前项或下一项。

决定修改列表的点与迭代循环相去甚远——调试器在两者之​​间的调用堆栈中显示了 40 个函数调用。因此,无法根据删除来修改迭代器。

我唯一能想到的就是在开始时复制列表并对其进行迭代,测试每个元素以确保它仍在主列表中。这是一个 O(n^2) 的命题,我想尽可能避免。

【问题讨论】:

  • 你确定不能修改迭代器吗?那将是 IMO 最干净的解决方案。您还可以尝试将元素设置为“过期”值,然后进行迭代以稍后将其删除。
  • 您可以改为使用shared_ptrs 列表,然后用对应的weak_ptrs 填充列表并对其进行迭代,检查每个weak_ptr 是否仍然有效。
  • 您可以在指向被擦除元素的迭代器上使用后自增运算符擦除,例如myList.erase(it++)。就像@Boris 在他的回答中所说,任何其他迭代器仍然有效。
  • @freitass,您是否错过了我所说的擦除调用发生 40 个函数调用远离循环本身的部分?这使您的解决方案有点不切实际。
  • 对不起,我想我做到了。

标签: c++ list iterator


【解决方案1】:

您有三个选择:

  1. 如您所说,制作列表的本地副本
  2. 当迭代器失效时重新开始(可能会跳过n 迭代?[使用continue])

  3. 编辑: 就像 Pubby 所说,标记删除的元素已过期,当你添加元素时使用跳过 n 迭代的东西:)

迭代器不允许通过索引进行访问,因此这使得为您的问题想出一个优雅的解决方案变得有点困难。

【讨论】:

    【解决方案2】:

    最终会使列表中的任何迭代器失效

    没有。

    std::list::erasedescription

    迭代器有效性

    • 迭代器、指针和引用引用被删除的元素 该功能无效。
    • 所有其他迭代器、指针和引用保持其有效性

    擦除的结论:只有被删除的元素变得无效。所有其他逗留有效。而且,在这种情况下,您有机会在大多数情况下实现 O(n)。

    【讨论】:

    • 我所说的“任何”的意思是可以选择列表中的任何项目进行删除,从而使指向它的迭代器无效。对不起,如果我感到困惑。
    【解决方案3】:

    这里有更多选项。

    1。拼接

    “修改”代码可以使用splice 方法将其移动到某个临时列表,而不是删除元素。拼接单个元素是一个恒定时间的操作。而且它不会使任何迭代器或引用无效(即使对于移动的元素)。

    在移动一个元素之前,“修改”代码应该推进列表迭代器的主副本(属于“迭代”代码)。

    “迭代”代码应该在每次迭代后清除这个临时列表。

    优点:无需为包含的元素添加任何标志。

    缺点:由于需要清除临时列表而导致性能下降;需要外部接口来推进属于“迭代”代码的迭代器;如果“迭代”和“修改”代码之间的某些代码需要检查相对于“已移除”元素的下一个/上一个元素,它只会看到其他“已移除”元素而不是列表的其余部分。

    2。带“锁定”标志的拼接

    如果您为迭代器当前指向的元素设置“锁定”标志,“修改”代码可能只对这个单个元素使用拼接,并以通常的方式删除其他元素。

    “迭代”代码应该只是在每次迭代后清除临时列表。

    优点:几乎不会影响性能。

    缺点:需要修改列表的元素;需要外部接口来推进属于“迭代”代码的迭代器;如果“迭代”和“修改”代码之间的某些代码需要检查相对于“已移除”元素的下一个/上一个元素,它什么也找不到。

    3。 “锁定”和“删除”标志

    如果您为迭代器当前指向的元素设置“锁定”标志,“修改”代码可能只是为该单个元素设置“已移除”标志并以通常的方式移除其他元素。

    “迭代”代码应该(在每次迭代之后)只删除一个标记为要删除的元素。

    优点:几乎不会影响性能;如果“迭代”和“修改”代码之间的某些代码需要检查相对于“已移除”元素的下一个/上一个元素,它会按预期工作。

    缺点:需要修改列表的元素。

    【讨论】:

      【解决方案4】:

      想到了两种可能的方法(除了制作列表的副本):

      1) 不要立即从列表中删除内容。相反,构建 另一个 迭代器容器以在您完成完整迭代后擦除。这确实会改变行为(“已删除”元素可能仍会被执行)所以我不知道它是否对你有帮助。

      2) 让您的列表包含指针或带有项目/有效性标志的一对,并简单地清除有效性。然后,在您完成第一次迭代后,再进行一次迭代以清理已停用的节点。

      【讨论】:

        猜你喜欢
        • 2021-10-13
        • 1970-01-01
        • 1970-01-01
        • 2010-09-13
        • 2011-06-10
        • 2020-11-22
        • 1970-01-01
        • 2012-05-07
        • 2011-09-07
        相关资源
        最近更新 更多