【问题标题】:Erase elements from STL multiset with iterator使用迭代器从 STL 多重集中擦除元素
【发布时间】:2013-01-28 13:41:57
【问题描述】:

我在一个单独的数据结构中维护一组多集容器的迭代器。过了一会儿,我从这个数据结构中选择了一个迭代器,然后从多重集中删除了与该迭代器相关的元素。我先用这样的东西:

#include <iostream>
#include <set>

int main ()
{
  std::multiset<int> myints;
  std::cout << "0. size: " << myints.size() << '\n';

  for (int i=0; i<10; i++) myints.insert(i);
  std::cout << "1. size: " << myints.size() << '\n';

  myints.insert (5);
  std::cout << "2. size: " << myints.size() << '\n';

  std::multiset<int>::iterator it = myints.find(5);
  myints.erase (it);
  std::cout << "3. size: " << myints.size() << '\n';
  myints.erase (it);
  std::cout << "4. size: " << myints.size() << '\n';
  return 0;
}

然而,事实证明第二个myints.erase (it); 导致分段错误。因此,我更改为以下代码并且它可以工作。我想知道这是否是好方法或可行undefined 情况:

int main ()
{
  std::multiset<int> myints;
  std::cout << "0. size: " << myints.size() << '\n';

  for (int i=0; i<10; i++) myints.insert(i);
  std::cout << "1. size: " << myints.size() << '\n';

  myints.insert (5);
  std::cout << "2. size: " << myints.size() << '\n';

  std::multiset<int>::iterator it = myints.find(5);
  myints.erase (it);
  std::cout << "3. size: " << myints.size() << '\n';

  std::multiset<int>::iterator newit = myints.find(*it);
  myints.erase (newit);
  std::cout << "4. size: " << myints.size() << '\n';

  return 0;
}

【问题讨论】:

  • 这两种方法都有错误 - 你能更清楚地解释你真正想要做什么吗?
  • 我使用多集容器构建了一个排序列表。因此,我需要在不知道它们的键的情况下从排序列表的中间删除一些元素。例如 1 2 3 4 的排序列表。过了一会儿,我需要在不知道其键的情况下从排序列表中删除一个元素(如中间元素)。因此,我维护了一组迭代器,指向单独数据结构中排序列表的每个元素,稍后,我将选择一个迭代器并从排序列表中删除相关元素。
  • 是的,但是您需要删除哪些?移除元素的条件是什么?
  • 如果你不知道key怎么删除元素?你怎么知道你要删除什么?
  • 在任何插入或擦除操作之后,位于外部数据结构中的任何迭代器都可能失效。你不能那样做。

标签: c++ data-structures stl multiset


【解决方案1】:

erase(it) 使迭代器 it 无效,即在 erase 之后它是无用的,并且对其进行任何操作都会导致未定义的行为。 (当它指向的元素被擦除时,您可能希望它“移动到下一个元素”,但事实并非如此。)

您的第二种方法无法解决此问题。它可能会偶然起作用,但在删除 it 后,您仍在重复使用它。


编辑: 鉴于您的描述“我只想从多重集中擦除一个 5,并在擦除后保持它对下一次擦除有效。”,您可以通过创建迭代器的副本来做到这一点,增加原件,然后擦除副本:

it = myints.find(5);
// better add a check here to make sure there actually is a 5 ...
std::multiset<int>::iterator newit = it;
it++;
myints.erase(newit);

由于您已经增加了it,它仍然有效,因为它没有指向被erase 杀死的元素。

但是,老实说,我无法想象在这种情况下这实际上可能有用,或者更确切地说,是必需的。

【讨论】:

  • 在erase(it) 之后有什么方法可以维持它的活动状态(指向具有相同键的下一个元素)?
  • 可能没有具有“相同键”的下一个元素(我猜你的意思是等效的下一个元素 - 这是一个多重集,而不是多重映射。)。你实际上想做什么?从多组中删除所有五个?
  • 我只想从多重集中擦除一个 5 并在擦除后保持其有效以进行下一次擦除。
  • @ARH 适用于什么?您刚刚删除了它所引用的元素。
  • @jmucchiello 鉴于对我的回答的第一条评论,我认为他的意思是“在删除的元素之后直接指向元素”,尽管我质疑这对于像 multiset 这样的排序结构的实用性.
【解决方案2】:

在您的第一种方法中,当您删除了该迭代器指向的元素时,迭代器将变得无效,然后当您尝试使用相同的迭代器再次擦除时,您将遇到分段错误。
在您的第二种方法中,因为每次您在擦除后进行查找时都会为您提供正确的迭代器。

您可以通过对代码进行以下更改来解决第一种情况。后增量运算符将返回一个新对象并将迭代器移动到下一个位置。我还建议在擦除之前进行结束检查,否则您可能会出现未定义的行为。

      std::multiset<int>::iterator it = myints.find(5);
      if(it != myints.end())
      myints.erase (it++);
      std::cout << "3. size: " << myints.size() << '\n';
      if(it != myints.end())
      myints.erase (it++);
      std::cout << "4. size: " << myints.size() << '\n';

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-04-07
    • 2014-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-22
    • 2015-06-30
    相关资源
    最近更新 更多