【问题标题】:How can I delete elements of a std::map with an iterator?如何使用迭代器删除 std::map 的元素?
【发布时间】:2011-06-03 18:55:00
【问题描述】:

我想遍历std::map 并根据其内容删除项目。如何做到最好?

【问题讨论】:

  • 您能否举例说明您的地图包含哪些内容以及您想使用哪些标准?一个典型的做法是遍历map的内容,调用map.erase(iterator);
  • @birryree 它甚至不必是地图......它可以是一个向量或其他可以使用迭代器的东西。我只是在寻找像@templatetypedef's 这样的通用答案。
  • @MartinYork 类似,但我问如何进行循环,而他只是想知道他的循环是否可以工作。

标签: c++ map iterator stdmap


【解决方案1】:

如果您有兼容 C++11 的编译器,这里有一个简单的方法:

std::map<K, V>::iterator itr = myMap.begin();
while (itr != myMap.end()) {
    if (ShouldDelete(*itr)) {
       itr = myMap.erase(itr);
    } else {
       ++itr;
    }
}

这个想法是让迭代器从容器的开始向前移动到结束,在每一步检查当前的键/值对是否应该被删除。如果是这样,我们使用erase 成员函数删除迭代过的元素,然后返回映射中下一个元素的迭代器。否则,我们将迭代器正常向前推进。

如果您没有兼容 C++11 的编译器,或者您使用的是较旧的代码库,那么事情就有点棘手了。在 C++11 之前,erase 成员函数不会返回指向映射中下一个元素的迭代器。这意味着为了在迭代时移除元素,您需要使用三部分舞蹈:

  1. 复制当前迭代器。
  2. 将当前迭代器前进到下一个元素。
  3. 在旧迭代器的副本上调用 erase

这里显示:

std::map<K, V>::iterator itr = myMap.begin();
while (itr != myMap.end()) {
    if (ShouldDelete(*itr)) {
       std::map<K, V>::iterator toErase = itr;
       ++itr;
       myMap.erase(toErase);
    } else {
       ++itr;
    }
}

这个过程是必需的,因为如果你只是在迭代器上调用erase,你会使其无效,这意味着像递增和递减这样的操作会导致未定义的行为。上面的代码通过设置迭代器的副本来解决这个问题,推进itr 使其位于下一个元素,然后擦除迭代器的临时副本。

使用一些巧妙的技巧,可以以牺牲可读性为代价缩小此代码。以下模式在较旧的 C++ 代码中很常见,但在 C++11 中不是必需的:

std::map<K, V>::iterator itr = myMap.begin();
while (itr != myMap.end()) {
    if (ShouldDelete(*itr)) {
       myMap.erase(itr++);  // <--- Note the post-increment!
    } else {
       ++itr;
    }
}

这里使用后自增运算符是制作旧迭代器副本的一种巧妙方法(请记住,后缀 ++ 运算符返回原始迭代器值的副本),同时也推进旧迭代器。

【讨论】:

  • 所以如果我有一个像这样的向量:10, 11, 12, 13,并且迭代器在递增时删除了 11,它不会跳过 12?...如果这有意义的话。因为我想知道当我删除 11 时,12 是否会移动到 11 的位置,而我会错过 12...
  • 我想你只是在评论@Timo 的回答时回答了这个问题,但如果你能澄清一下,那就太好了。
  • 对于向量的情况,逻辑是不同的(见下面 Timo 的例子);在您擦除()某些东西之后,擦除点处或之后的所有迭代器都将失效。因此,要知道从哪里获取,您可以捕获 erase() 函数的值。使用映射,擦除中唯一无效的迭代器是被删除元素的迭代器。
  • 这是一个很好的答案。唯一缺少的是将其与 std::vector 行为进行比较并进行对比,但我想这会涉及到一个不同的主题。
【解决方案2】:
for(MyMap::iterator it = mymap.begin(); it!=mymap.end(); ) {
  if(mycondition(it))
    it = mymap.erase(it);
  else
    it++;
}

编辑:似乎这仅适用于 MSVC

edit2:在 c++0x 中,这也适用于关联容器

【讨论】:

  • 这将适用于像 std::vector 这样的序列容器,但不适用于像 std::map 这样的关联容器。不同之处在于,在序列容器中,erase() 返回一个指向下一个元素的迭代器,而在关联容器中,它返回 void。
  • @templatetypedef:嗯,这很有趣,因为 MSVC 中的关联容器的工作方式不同:msdn.microsoft.com/en-us/library/z2f3cb7h.aspx 我猜那是非标准的。
  • 刚刚从 ISO 规范的表 69 中确认它应该返回 void。我猜 MSVC 版本是非标准的……我从来不知道!
  • 它现在可以在 C++11 中使用。但是,仍然有许多编译器没有为 C++11 做好准备。所以mymap.erase(it++) 更便携。
【解决方案3】:


这是一种简单的方法:

    int value_to_delete( 2 );
    for( std::map<int, int>::iterator i = mm.begin(); i != mm.end(); ) {
        if( i->second != value_to_delete ) {
            mm.erase( i++ ); // advance before iterator become invalid
        }
        else {
            ++i;
        }
    }

【讨论】:

    猜你喜欢
    • 2015-05-15
    • 1970-01-01
    • 2013-05-25
    • 2014-01-04
    • 2011-02-21
    • 2021-05-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多