删除循环内向量的元素


70

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

“播放器=”功能在“播放器”中不可用。

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

我该怎么办?

更新:您是否认为带有指针成员的问题vector :: erase属于同一问题?因此,我需要一个赋值运算符吗?为什么?


2
请注意,使用std :: remove_if可能会更好。请参阅这篇文章以了解更多信息。

在描述使用擦除/删除成语这个职位。
Mukesh MV

Answers:


134

您不应itfor循环中递增:

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++那里不需要,因为itfor-body本身在增加。

对于错误“ 'operator ='函数在'Player'中不可用”,它来自erase()内部使用operator=来移动向量中元素的用法。为了使用erase(),类的对象Player必须是可分配的,这意味着您需要operator=Player类实现。

无论如何,您应尽可能避免使用原始循环1,而应改用算法。在这种情况下,流行的“删除擦除惯用语”可以简化您的操作。

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

1.这是我看过的肖恩·帕特里纳(Sean Parent)最好的演讲之一。


1
我尝试了这个,但是我收到了同样的错误。当我删除上述循环(删除)时,程序会编译。因此,删除/擦除存在问题。有些类Player的成员是指向其他对象的指针。在这种情况下,它们变成了什么?
arjacsoh 2011年

1
实际上,错误来自std :: vector.erase,它使用赋值运算符移动元素以保持向量连续。
ronag 2011年

这个成语有名字吗?
sp2danny

这是一个可怕的答案!删除元素后,迭代器将失效!!!!
量子物理学家

8
@TheQuantumPhysicist:是的,这是真的,这就是我这样做的原因: it = allPlayers.erase(it);请仔细查看作业!否则随时发布更好的答案。
纳瓦兹

15
if(allPlayers.empty() == false) {
    for(int i = allPlayers.size() - 1; i >= 0; i--) {
        if(allPlayers.at(i).getpMoney() <= 0) {
            allPlayers.erase( allPlayers.begin() + i ); 
        }
    }
}

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


3
快速评论一下:您只需说(!allPlayers.empty())即可替换(allPlayers.empty()== false)。这是因为empty()返回一个布尔类型:如果向量为空,则将返回true。使用“ not”运算符就像说“如果向量为空不是真的”。只是为了美化您的代码:)
Floella

@Anarelle谢谢!
Dawoon Yi

这提醒我,我不应该从头开始擦除(i == 0)。因为每次都调用一次Erase(),所以begin()将同时被更改。begin()+我将根据新向量进行更改(仅删除了一项)。如果从头到尾擦除,就可以了。谢谢:)
galian

1
这将产生有效的结果,但效率可能不高,因为对于每个移除的元素,后续元素将重复地朝前移动。
阿空加瓜

10

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

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

3
+1。这是要走的路

34
-1为不明显的答案。例如,在不知道如何在较低级别进行算法的情况下,如何编写所述算法。不是每个人都可以住在抽象天堂中。与回答USE JQUERY!!1!尝试学习Java脚本的人一样有用。
Thomas Eding 2013年

3
仅当您只想删除元素时,此算法才有用。考虑场景if(condition) it = x.erase(it); else { file << *it; ++it; }。如您所见,如果元素不适合删除时是否要执行其他操作,则不能使用remove_if。即使使用它,也可能必须再次遍历循环。
iammilind

5

您的特定问题是您的Player班级没有赋值运算符。您必须使“ 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文件中,但仍然出现错误(带有不同的消息)。最后需要复制构造函数吗?
arjacsoh 2011年

1
您还应该实现一个复制构造函数。如果您不发布相关的错误或代码,则很难说出问题出在哪里。
ronag 2011年

3

或向后循环。

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

3

C ++ 11引入了新的函数集合,这些函数将在此处使用。

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

然后,您将获得不必对末端元素进行太多调整的优势。


2
std::vector::erase(iterator)删除迭代器指向的单个元素。在您的示例中,它将尝试删除由std::remove_if-返回的迭代器指向的元素,该迭代器是一个传递式迭代器,因此几乎可以肯定这是不正确的(并会导致崩溃)。应该为:allPlayers.erase(std::remove_if(...), allPlayers.end())而是删除范围内的所有元素。
Ellis

0

答案较晚,但看到无效的变体:

  1. std::remove还是std::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());

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.