2

在调试向量时,我发现不一致。假设以下代码试图从只有一个元素的向量中删除一个条目

#include <iostream>
#include <vector>
std::vector<int> v;
void myremove(int);
int main()
{
  v.push_back(10); 
  std::cout << "10 pushed back\n";
  myremove(10);
  std::cout << "done :)\n";
  return 0;
}

void myremove( int a )
{
  std::vector<int>::iterator it = v.begin();
  int counter = 0;
  for ( ; it != v.end(); it++ ) {
    std::cout << "iterating for " << counter << " times and vector size is " << v.size() << "\n";
    if ( a == (*it) ) {
      v.erase(it);
      std::cout << "removed " << a << "\n";
    }
    ++counter; 
  }
}

这是我在输出中看到的:

 $ g++ test.cpp 
 $ ./a.out | more
 10 pushed back
 iterating for 0 times and vector size is 1
 removed 10
 iterating for 1 times and vector size is 0
 iterating for 2 times and vector size is 0
 iterating for 3 times and vector size is 0
 iterating for 4 times and vector size is 0
 iterating for 5 times and vector size is 0
 iterating for 6 times and vector size is 0
 ....
 ....
 iterating for 33790 times and vector size is 0
 Segmentation fault

我的理解是,当元素被删除时,大小将变为 0,但是迭代器移动了一步,它仍然试图到达终点,但他不知道他已经通过了终点。

有人可以解释更多发生了什么以及如何避免这种情况吗?

4

6 回答 6

6

erase()对迭代器的调用it无效后:

迭代器和对已擦除元素以及它们与容器末端之间的元素的引用无效。

设置it为的返回值,erase()并且仅在未发生删除时才增加:

while (it != v.end())
{
    if ( a == (*it) )
    {
        it = v.erase(it);
        std::cout << "removed " << a << "\n";
    }
    else
    {
        ++it;
    }
}

其中的返回值为erase()

最后一个被移除元素之后的迭代器。

您可以使用以下方法std::remove_if()代替手动编码循环来擦除元素:

v.erase(std::remove_if(v.begin(),
                       v.end(),
                       [](const int i) { return i == 10; }),
        v.end());
于 2013-04-12T14:21:29.903 回答
4

当你抹去

v.erase(it);

您的迭代器不再有效。您必须使用从擦除返回的迭代器。Erase 为您提供了一个迭代器,该迭代器指向调用擦除的元素之后的元素。如果在循环递增之前擦除了最后一个元素,则必须中断循环it

it = v.erase(it);
if(it == v.end())
    break;

建议:您可以继续将 for 循环更改为 while 循环。并显式地增加迭代器(即只有当你没有擦除任何东西时。如果你已经擦除了迭代器,那么迭代器就已经增加了)。

像这样

while(it != v.end()) {
    if ( a == (*it) ) 
      it = v.erase(it);
    else
      ++it; 
}
于 2013-04-12T14:21:20.977 回答
0

每次插入和擦除都会使容器的所有迭代器失效。这些方法在插入/擦除后返回唯一有效的迭代器。

于 2013-04-12T14:21:19.730 回答
0

在文档中std::vector::erase

迭代器有效性

指向位置(或第一个)及以后的迭代器、指针和引用无效,所有迭​​代器、指针和对位置(或第一个)之前元素的引用都保证继续引用它们在调用之前引用的相同元素。

您擦除循环中的元素(取决于迭代器)会使一切变得疯狂。差不多就是这样!

于 2013-04-12T14:21:40.457 回答
0

当你调用擦除函数时,迭代器不再是有效的迭代器,当你增加无效的迭代器时,你会得到虚假的结果。

于 2013-04-12T14:23:01.007 回答
0

错误是您期望在对迭代器进行擦除之后,该迭代器仍将处于一致状态。情况并非如此,您的代码准确地说明了发生这种情况时的情况。

您的函数的语义是删除向量中等于 的所有元素a。您可以通过过滤向量来获得相同的结果。关于这一点,请参阅该问题:

如何使用特定过滤器从其他向量制作 std::vector?

于 2013-04-12T14:29:06.233 回答