用迭代器从列表中删除一个元素

8

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


1
请注意,这样使用迭代器违反了其作为设计模式的使用方式,但这并不意味着在许多情况下不是正确的做法(例如查看集合中的元素)。 - Jeremy
https://dev59.com/S3RB5IYBdhLWcg3wiHpl - nielsj
@Jeremy:实际上,这并不违反将迭代器作为设计模式的使用。然而,它在迭代器失效方面的语义较差。在许多容器中,删除一个项目会使所有迭代器无效,包括您当前用于迭代元素的迭代器。 - André Caron
@Andre,是的,但我总是会使用list,这不会使其他迭代器无效,即使你从中删除一个项目。另外@nj,我认为这与本问题无关。 - Seth Carnegie
6个回答

3

编辑:

你不能使用单个迭代器。

如果你有begin/end迭代器,你可以使用 std::remove 算法将所有要删除的元素移动到末尾,并在稍后的时间点删除它们。

如果你没有这些迭代器,或者以上方法在你当前的设计中不可行,我建议修改你的函数以接受一个 std::pair<std::list<T>, std::list<T>::iterator> 或类似的东西。


2
但它也需要列表。 - Daniel
1
std::remove()函数不能从容器中删除项目,因为它无法做到。请查看erase-remove惯用语 - André Caron
@André - 这就是我说的。使用它将它们移动到末尾并在以后删除它们。 - Node
我决定做类似的事情,即在它所持有的项中仅存储对拥有列表的引用。这个解决方案对现有代码的更改很小(我想只有两行)。但如果我不能这样做,我会按照这个答案所建议的方式(使用std::pair)来实现,因此我将把它标记为答案。 - Seth Carnegie
虽然这在原则上是正确的(我已经点赞了),但请注意std::remove()不一定会将要删除的对象移动到末尾。它对它们发生的事情没有任何声明。对于std::vector,我确信所有实现都只会覆盖它们。对于std::list,我不确定。 - sbi

3
不可以。迭代器是轻量级对象,类似于指针,不携带所引用容器的引用(尽管某些实现在调试模式下会这样做)。就像只有一个指向数组的指针时无法从数组中“删除”对象一样,没有访问容器的情况下也无法从容器中删除对象。

3

1

虽然其他人提到你做不到它,但我认为我可以解释为什么。

我相信特定的技术原因(而不是设计原因)是由于列表进行了一些维护工作,例如跟踪大小等,这要求必须通过它们传递某些操作来允许进行维护。

正因为如此,任何可能提供的黑客都很可能失败。


这是一个好想法,不过似乎你可以让迭代器告诉它们的父列表它们正在删除自己或其他什么操作。 - Seth Carnegie

0

因为list是一个双向链表,您是否可以编写一个函数来操作nextprevious指针以删除元素?或者那样做会非常糟糕吗?还是只是因为成员是私有的? - Seth Carnegie
@Seth:是的,从技术上讲这是可能的。例如,在Java中,迭代器具有"remove方法"。但这不是C++迭代器设计的一部分。我猜标准委员会更倾向于与其他容器保持统一性。 - André Caron
@Seth:这可能是针对特定供应商库的特定版本完成的。但是,这意味着有很多非标准(绝对不可移植)的代码。此外,成员可能是私有的。 - André Caron

-1

您可以手动完成此操作。迭代器将_M_node公开为当前节点。您可以执行以下操作:

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

2
这只适用于特定版本的特定实现中的特定容器中的特定迭代器。这是一个实现细节,甚至可以通过使用正确的预处理器标记来关闭。此外,它可能会破坏容器的内部结构,您需要了解这一点才能进行操作。 - sbi
1
更不用说你没有更新 _M_next_M_prev 指针(因为它是一个双向链表)。我也考虑过这个问题,但是这段代码需要在不同的版本/编译器上工作。 - Seth Carnegie
4
请注意,即使这是可能的,这也会导致内存泄漏。您还需要访问容器的分配器来释放内存,并且如果缓存了列表项,您还需要减少列表项数(标准允许实现在O(1) size()和O(1) splice()之间选择)。 - André Caron

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接