【问题标题】:Remove elements of a vector inside the loop删除循环内向量的元素
【发布时间】:2012-01-27 13:47:38
【问题描述】:

我知道有与此类似的问题,但我没有设法在他们的帮助下找到我的代码的方法。我只想通过检查循环内该元素的属性来删除/删除向量的元素。我怎样才能做到这一点?我尝试了以下代码,但收到了模糊的错误消息:

'operator ='功能在'Player'中不可用。

 for (vector<Player>::iterator it = allPlayers.begin(); it != allPlayers.end(); it++)
 {
     if(it->getpMoney()<=0) 
         it = allPlayers.erase(it);
     else 
         ++it;
 }

我该怎么办?

更新:您认为问题vector::erase with pointer member 属于同一个问题吗?因此我需要一个赋值运算符吗?为什么?

【问题讨论】:

  • 请注意,使用 std::remove_if 会好很多。有关详细信息,请参阅this 帖子。
  • 使用this 帖子中描述的擦除/删除习语。

标签: c++ vector erase


【解决方案1】:

您不应在for 循环中增加it

for (vector<Player>::iterator it=allPlayers.begin(); 
                              it!=allPlayers.end(); 
                              /*it++*/) <----------- I commented it.
{

   if(it->getpMoney()<=0) 
      it = allPlayers.erase(it);
  else 
      ++it;
 }

注意注释部分;那里不需要it++,因为it 在for-body 本身中会增加。

至于报错“'operator =' function is available in 'Player'”,是因为使用erase()内部使用operator=来移动vector中的元素。为了使用erase(),类Player的对象必须是可赋值的,这意味着你需要为Player类实现operator=

无论如何,您应该尽可能避免使用raw loop1,而应该更喜欢使用算法。在这种情况下,流行的Erase-Remove Idiom 可以简化您的工作。

allPlayers.erase(
    std::remove_if(
        allPlayers.begin(), 
        allPlayers.end(),
        [](Player const & p) { return p.getpMoney() <= 0; }
    ), 
    allPlayers.end()
); 

1.这是我看过的the best talks by Sean Parent 之一。

【讨论】:

  • 我试过这个,但我收到了同样的错误。当我删除上述循环(删除)时,程序编译。因此,删除/擦除存在问题。 Player 类的成员是指向其他对象的指针。在这种情况下他们会变成什么?
  • 实际上错误来自std::vector.erase,它使用赋值运算符移动元素以保持向量连续。
  • 这个成语有名字吗?
  • 这是一个可怕的答案!删除元素后迭代器失效!!!
  • @TheQuantumPhysicist:是的,这是真的,这就是我这样做的原因:it = allPlayers.erase(it); 请仔细看作业!否则,请随时发布更好的答案。
【解决方案2】:

忘记循环并使用 std 或 boost 范围算法。
使用 Boost.Range en Lambda 它看起来像这样:

boost::remove_if( allPlayers, bind(&Player::getpMoney, _1)<=0 );

【讨论】:

  • +1。这是the way to go
  • -1 表示不诚实的答案。例如,在不知道如何在较低级别执行的情况下如何编写所述算法。不是每个人都可以生活在抽象天堂。与尝试学习 Javascript 的人回答 USE JQUERY!!1! 一样有用。
  • 此算法仅在您只想删除元素时才有用。想想场景,if(condition) it = x.erase(it); else { file &lt;&lt; *it; ++it; }。正如您所看到的,当元素不适合删除时是否要执行其他操作,您不能使用remove_if。即使你使用它,你也可能不得不再次遍历循环。
【解决方案3】:

您的具体问题是您的 Player 类没有赋值运算符。您必须使“播放器”可复制或可移动,以便将其从矢量中删除。这是因为该向量需要是连续的,因此需要对元素重新排序,以填补删除元素时产生的空白。

还有:

使用标准算法

allPlayers.erase(std::remove_if(allPlayers.begin(), allPlayers.end(), [](const Player& player)
{
    return player.getpMoney() <= 0;
}), allPlayers.end());

如果你有提升,甚至更简单:

boost::remove_erase_if(allPlayers, [](const Player& player)
{
    return player.getpMoney() <= 0;
});

如果您不支持 C++11 lambda,请参阅 TimW 的回答。

【讨论】:

  • 我也认为问题是你提到的。但是,我添加了一个赋值运算符作为 Player& operator= (const Player& rhs);在 Player.h 文件中,但我仍然收到错误(带有不同的消息)。我最终需要一个复制构造函数吗?
  • 您还应该实现一个复制构造函数。如果您不发布相关错误或代码,则很难说出问题所在。
【解决方案4】:
if(allPlayers.empty() == false) {
    for(int i = allPlayers.size() - 1; i >= 0; i--) {
        if(allPlayers.at(i).getpMoney() <= 0) {
            allPlayers.erase( allPlayers.begin() + i ); 
        }
    }
}

这是我删除向量中元素的方法。 很容易理解,不需要任何技巧。

【讨论】:

  • 简短评论:您可以通过说 (!allPlayers.empty()) 来替换 (allPlayers.empty() == false)。这是因为 empty() 返回一个布尔类型:如果向量为空,它将返回 true。使用“not”运算符就像是在说“如果向量不为空”。只是为了美化你的代码:)
  • @Anarelle 谢谢!
  • 这提醒我不应该从头开始擦除 (i == 0)。因为每次调用erase(),那么begin()都会同时改变。 begin() + i 将根据新向量进行更改(刚刚删除了一项)。如果从头到尾擦除就OK了。谢谢:)
  • 这将产生有效的结果,但效率低下,因为后续元素将被重复地向前移动,每个元素被删除。
【解决方案5】:

或者向后循环。

for (vector<Player>::iterator it = allPlayers.end() - 1; it != allPlayers.begin() - 1; it--)
    if(it->getpMoney()<=0) 
        it = allPlayers.erase(it);

【讨论】:

    【解决方案6】:

    C++11 引入了一个新的函数集合,将在这里使用。

    allPlayers.erase(
        std::remove_if(allPlayers.begin(), allPlayers.end(),
            [](auto& x) {return x->getpMoney() <= 0;} ), 
        allPlayers.end()); 
    

    然后您就不必对末端元素进行如此多的移动了。

    【讨论】:

    • std::vector::erase(iterator) 删除迭代器指向的单个元素。在您的示例中,它将尝试删除 std::remove_if 返回的迭代器所指向的元素——这是一个传递结束的迭代器,因此这几乎肯定是不正确的(并且会导致崩溃)。它应该是:allPlayers.erase(std::remove_if(...), allPlayers.end()),而是删除范围内的所有元素。
    【解决方案7】:

    迟到的答案,但看到了低效的变体:

    1. std::removestd::remove_if 是要走的路。
    2. 如果由于任何原因这些不可用或不能用于任何其他原因,请执行这些隐藏的操作。

    高效移除元素的代码:

    auto pos = container.begin();
    for(auto i = container.begin(); i != container.end(); ++i)
    {
        if(isKeepElement(*i)) // whatever condition...
        {
            *pos++ = *i; // will move, if move assignment is available...
        }
    }
    // well, std::remove(_if) stops here...
    container.erase(pos, container.end());
    

    您可能需要显式编写这样的循环 e. G。如果您需要迭代器本身来确定是否要删除元素(条件参数需要接受对元素的引用,还记得吗?),例如。 G。由于与继任者/前任者的特定关系(如果这种关系是平等的,则有std::unique)。

    【讨论】:

      猜你喜欢
      • 2017-03-04
      • 1970-01-01
      • 1970-01-01
      • 2015-02-02
      • 1970-01-01
      • 2021-10-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多