【问题标题】:Removing item from vector while iterating?迭代时从向量中删除项目?
【发布时间】:2021-11-08 18:17:28
【问题描述】:

我有一个包含活动或非活动项目的向量。我希望此向量的大小保持较小以解决性能问题,因此我希望从向量中删除已标记为非活动的项目。我在迭代时尝试这样做,但出现错误“向量迭代器不兼容”。

vector<Orb>::iterator i = orbsList.begin();

    while(i != orbsList.end()) {
        bool isActive = (*i).active;

        if(!isActive) {
            orbsList.erase(i++);
        }
        else {
            // do something with *i
            ++i;
        }
    }

【问题讨论】:

标签: c++ stl iterator


【解决方案1】:

从向量中间删除项目将使该向量的所有迭代器无效,因此您不能这样做(更新:不求助于 Wilx 的建议)。

另外,如果您担心性能,从矢量中间擦除项目无论如何都是一个坏主意。也许您想使用std::list

【讨论】:

【解决方案2】:

您可以这样做,但我认为您将不得不重新调整您的while()erase() 函数返回一个迭代器,指向擦除后的下一个元素:iterator erase(iterator position);。引用 23.1.1/7 的标准:

a.erase(q) 返回的迭代器 立即指向元素 在元素存在之前跟随 q 抹去。如果不存在这样的元素, a.end() 被返回。

虽然也许您应该改用Erase-remove idiom

【讨论】:

【解决方案3】:

正如他们所说,无论您使用哪种形式的迭代器增量,vector 的迭代器都会在 vector::erase() 上失效。请改用整数索引。

【讨论】:

  • 使用erase 的返回值,你会得到一个有效的迭代器。所以只需要将返回的值赋给迭代器——这里没有性能问题。
  • @harper:实际上,这里有一个很大的性能问题;从向量中间擦除一个项目需要将其余所有项目都向下移动,这每次都涉及 O(N) 构造函数和析构函数调用。
  • 而使用索引有什么好处呢?擦除性能如何提高?我只是注释了语句“迭代器无效..无论哪种形式”...
【解决方案4】:

您可能需要考虑为您的数据结构使用std::list 而不是std::vector。将擦除与迭代结合使用会更安全(不易出错)。

【讨论】:

    【解决方案5】:

    过去我最易读的方法是使用std::vector::erasestd::remove_if。在下面的示例中,我使用此组合从向量中删除任何小于 10 的数字。

    (对于非 c++0x,您只需将下面的 lambda 替换为您自己的谓词:)

    // a list of ints
    int myInts[] = {1, 7, 8, 4, 5, 10, 15, 22, 50. 29};
    std::vector v(myInts, myInts + sizeof(myInts) / sizeof(int));
    
    // get rid of anything < 10
    v.erase(std::remove_if(v.begin(), v.end(), 
                           [](int i) { return i < 10; }), v.end());
    

    【讨论】:

    • 虽然在简单的情况下很优雅,但我怀疑它在现实世界中的有效性。这甚至不适用于动态大小向量。
    • "这对动态尺寸向量根本不起作用。"为什么不呢?
    • 虽然std::remove_if lambda 看起来不错,但如果我只想删除一个符合条件的元素,它仍然会遍历每个元素直到最后。因此,我更喜欢手动迭代,这样我就可以随时跳出循环。
    【解决方案6】:

    我同意 wilx 的回答。这是一个实现:

    // curFiles is: vector < string > curFiles;
    
    vector< string >::iterator it = curFiles.begin();
    
    while(it != curFiles.end()) {
    
        if(aConditionIsMet) {
    
            it = curFiles.erase(it);
        }
        else ++it;
    }
    

    【讨论】:

      【解决方案7】:

      如果有人需要处理索引

      vector<int> vector;
      for(int i=0;i<10;++i)vector.push_back(i);
      
      int size = vector.size();
      for (int i = 0; i < size; ++i)
      {
          assert(i > -1 && i < (int)vector.size());
          if(vector[i] % 3 == 0)
          {
              printf("Removing %d, %d\n",vector[i],i);
              vector.erase(vector.begin() + i);
          }
      
          if (size != (int)vector.size())
          {
              --i;
              size = vector.size();
              printf("Go back %d\n",size);
          }
      }
      

      【讨论】:

      • 为什么不只是 size-- i-- 旁边的擦除?
      • @user1122069 我的良好做法禁止这样的事情,很多人不明白--i nad i-- 之间的区别
      【解决方案8】:

      erase 返回指向下一个迭代器值的指针(与 Vassilis 相同):

      vector <cMyClass>::iterator mit
      for(mit = myVec.begin(); mit != myVec.end(); )
      {   if(condition)
              mit = myVec.erase(mit);
          else
              mit++;
      }
      

      【讨论】:

      • 使用*mit访问迭代器位置的值
      猜你喜欢
      • 1970-01-01
      • 2014-10-26
      • 1970-01-01
      • 2010-12-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多