【问题标题】:Removing an element from a list with only its iterator从列表中删除仅包含其迭代器的元素
【发布时间】:2011-10-16 14:57:46
【问题描述】:

如果您只有指向要删除的元素的迭代器,是否可以从std::list 中删除元素?我有大量使用迭代器来列出元素的函数,如果必须将拥有的 list 传递给它们每个人,那将是一个巨大的不便。

【问题讨论】:

  • 请注意,以这种方式使用迭代器与将其用作设计模式是相悖的,但这并不是说在许多情况下(例如查看集合的元素)不是正确的做法。
  • @Jeremy:实际上,这并不是反对将迭代器用作设计模式。但是,它在迭代器失效方面的语义很差。在许多容器中,删除一项会使所有迭代器失效,包括您当前用于迭代元素的迭代器。
  • @Andre 是的,但我总是会使用list,如果你从中删除一个项目,它不会使其他迭代器失效。还有@nj,我认为这与这个问题无关。

标签: c++ list iterator std


【解决方案1】:

编辑:

你不能使用单个迭代器。

如果您有开始/结束迭代器,您可以使用std::remove 算法将所有要擦除的元素移动到末尾,并在稍后删除它们。

如果您不这样做,或者上述方法在您当前的设计中不可行,我建议您更改您的功能以采用 std::pair<std::list<T>, std::list<T>::iterator> 或类似的东西。

【讨论】:

  • 但它也需要列表。
  • std::remove() 函数不会从容器中删除项目,因为它不能。查看erase-remove idiom。
  • @André - 我就是这么说的。使用它将它们移动到末尾并在稍后删除它们
  • 我决定做一些类似的事情,它只是在它持有的项目中存储对拥有列表的引用。该解决方案涉及对现有代码的最小更改(我认为是两行)。但如果我不能这样做,我会按照这个答案(std::pair 之一)中的建议做,所以我会将此标记为答案。
  • 虽然这是正确的原则上(我已经赞成),但请注意std::remove() 不一定将要删除的对象移动到最后。它没有声称他们会发生什么。对于std::vector,我确信所有的实现都会覆盖它们。对于std::list,我不确定。
【解决方案2】:

不,你不能。迭代器是仿照指针的轻量级对象,不携带对它们所引用的容器的引用。 (虽然有些实现在调试模式下在内部这样做。)

就像当你只有一个指向数组的指针时你不能从数组中“删除”一个对象一样,你不能在没有访问容器的情况下从容器中删除一个对象。

【讨论】:

    【解决方案3】:

    你不能用标准库做到这一点,但你可以使用 Boost 的侵入式列表http://www.boost.org/doc/libs/1_37_0/doc/html/boost/intrusive/list.html,它有这样一个接口。

    【讨论】:

      【解决方案4】:

      虽然其他人提到你不能这样做,但我想我可以提供原因。

      我认为具体的技术原因(而不是设计原因)是列表进行了一些维护,例如跟踪大小,这要求必须通过它们传递某些操作才能进行维护。

      正是由于这个原因,任何可能提供的 hack 都可能失败。

      【讨论】:

      • 这是个好主意,尽管您似乎可以让迭代器告诉他们的父列表他们正在删除自己或其他东西。
      【解决方案5】:

      不,这是不可能的。顾名思义,迭代器的工作是遍历序列的元素。查看SGI page on iterators,了解 C++ 标准库中的迭代器设计摘要。

      【讨论】:

      • 因为list 是一个双向链表,你能不能写一个函数来操作next 和previous 指针以便它删除元素?或者那真的很糟糕?还是只是成员是私人的?
      • @Seth:是的,这在技术上是可行的。例如,在 Java 中,迭代器有一个remove method。但是,这不是 C++ 迭代器设计的一部分。我猜标准委员会更喜欢与其他容器保持一致。
      • @Seth:这可能针对特定供应商库的特定版本进行。然而,这意味着大量的非标准(而且绝对是不可移植的)代码。此外,成员可能是私人的。
      【解决方案6】:

      您可以手动执行此操作。迭代器将_M_node 公开为当前节点。你可以这样做:

      itr._M_node->_M_prev->_M_next = itr._M_node._M_next;
      

      【讨论】:

      • 这只是特定实现的特定版本中特定容器的特定迭代器的情况,这是一个实现细节,甚至可以通过使用正确的预处理器令牌来关闭。此外,它可能会弄乱容器的内部结构,您需要知道这一点才能做到这一点。
      • 更不用说你没有更新_M_next的_M_prev指针(因为它是一个dlinked list)。这就是我的想法,但这段代码也需要与不同的版本/编译器一起使用。
      • 请注意,即使这是可能的,这也会导致内存泄漏。您还需要访问容器的分配器以释放内存并且如果列表被缓存,则需要减少列表的项目计数(标准允许实现在 O(1) size() 和O(1)splice())。
      猜你喜欢
      • 1970-01-01
      • 2016-07-19
      • 2013-11-25
      • 1970-01-01
      • 2018-07-18
      • 1970-01-01
      • 2011-04-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多