C++ 删除元素时std::vector中可能存在不一致

C++ 删除元素时std::vector中可能存在不一致,c++,vector,C++,Vector,在调试向量时,我看到了不一致性。假设下面的代码试图从只有一个元素的向量中删除一个条目 #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 <<

在调试向量时,我看到了不一致性。假设下面的代码试图从只有一个元素的向量中删除一个条目

#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; 
  }
}
我的理解是,当元素被移除时,大小将变为0,但是迭代器移动了一步,仍然试图到达终点,但他不知道他已经通过终点


有人能解释一下发生了什么以及如何避免这种情况吗?

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

擦除时

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

it = v.erase(it);
if(it == v.end())
    break;
建议:您可以继续并将for循环更改为while循环。并显式地递增迭代器(即,仅当您未擦除任何内容时。如果您已擦除迭代器,则迭代器已递增)

像这样

while(it != v.end()) {
    if ( a == (*it) ) 
      it = v.erase(it);
    else
      ++it; 
}
调用迭代器
后,迭代器
无效:

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

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

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

std::vector::erase
的文档中:

迭代器有效性


删除循环中的一个元素(依赖于迭代器)会让一切变得疯狂。差不多就是这样

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

错误在于,在对迭代器执行擦除操作后,您期望该迭代器仍处于一致状态。事实并非如此,您的代码精确地说明了发生这种情况时的情况

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


您对此进行了测试吗?但我仍然得到相同的输出“改为将其设置为erase()的返回值”-并在它之后比较
it
v.end()
,否则您可能会增加迭代器,指向向量的末尾。否。擦除仅使从擦除点开始到结束的迭代器失效。如果新大小大于当前容量,则insert仅使所有迭代器无效。否则,只有从插入点开始的迭代器才无效。那么,对于未排序的容器,例如无序的_集和无序的_映射,又如何呢?一般来说,您永远不知道前面的迭代器是否有效,因此您应该始终丢弃其他迭代器并使用返回的迭代器。这是一个很好的策略。我唯一的一点是你回答中的陈述是错误的。该标准对迭代器何时失效做出了某些保证,而您的声明与这些保证相矛盾。您是对的,我们的讲座幻灯片是假的。然而,迭代器的行为在很大程度上取决于容器。例如,列表迭代器根本不会失效。是的,确实如此。我的评论只提到了这个问题所涉及的
std::vector
。然而,在所有标准容器中,
std::vector
具有最严格的失效规则,在大多数情况下,它与
std::basic_string
共享。也就是说,对于除向量或字符串之外的任何其他标准容器,迭代器失效的情况更少。在某些情况下,正如您所指出的,迭代器永远不会失效(除非迭代器指向的对象实际上已被销毁)。您测试过吗?我还是一样output@mahmood我已经修好了。看一看。微优化:使用
++it
而不是
it++
while (it != v.end())
{
    if ( a == (*it) )
    {
        it = v.erase(it);
        std::cout << "removed " << a << "\n";
    }
    else
    {
        ++it;
    }
}
v.erase(std::remove_if(v.begin(),
                       v.end(),
                       [](const int i) { return i == 10; }),
        v.end());