for循环中的向量擦除函数不能正确地擦除类的向量

时间:2013-08-06 14:33:53

标签: c++ class for-loop vector erase

我有一个简单的for循环:

for (int i = 0; i < c.numparticles; i++)
{
    if ( labs((noncollision[i].getypos())) > 5000 )
    {
        noncollision.erase (noncollision.begin()+i);
    }
}

其中noncollision是类particle的向量。在此特定示例中,应删除noncollision大于5000的任何ypos。我一直在使用noncollision大小为6,其中2个ypos大于5000.但是,这个for循环只删除其中一个,完全忽略了另一个。我怀疑是因为noncollision是一个类的向量,这个类是以某种方式受到保护,还是导致数组函数采取不同的行为?以下是noncollisionparticle的声明:

vector<particle> noncollision;

class particle{
private:
int xpos;
int ypos;
int xvel;
int yvel;
bool jc; // Has the particle just collided?
public:
etc....
};

有人能解释为什么会这样,以及如何纠正它?我是否需要为particle类设置“擦除功能”?

3 个答案:

答案 0 :(得分:4)

如果你有两个候选元素彼此相邻(例如,在i=5i=6),那么你跳过第二个,因为你刚刚删除了一个在i=5 ...然后第二个成为 i=5,但您在下一个循环中增加i以获得i=6

您需要修复循环以正确支持您同时从正在迭代的同一容器中删除元素的事实。

通常你会使用实际的迭代器(而不是计数器i),并且vector::erase方便地返回一个新的迭代器供你在下一次迭代中使用:

vector<particle>::iterator it = noncollision.begin(), end = noncollision.end();
for ( ; it != end; ) { // NB. no `++it` here!
    if (labs(it->getypos()) > 5000) {
       // erase this element, and get an iterator to the new next one
       it  = noncollision.erase(it);

       // the end's moved, too!
       end = noncollision.end();
    }
    else {
       // otherwise, and only otherwise, continue iterating as normal
       it++;
    }
}

然而,引用Joe Z:

  

此外,由于erase可以是向量大小的O(N),你也可以(a)使用反向迭代器对循环进行基准测试,(b)考虑将未被擦除的元素复制到一个新的向量而不是从中间删除元素,或者(c)使用list<>而不是vector<>如果从中间删除是一种常见的操作。


或者,如果你很懒,你也可以反转你的迭代顺序,在这种特殊情况下保留计数器i的神圣性:

for (int i = c.numparticles-1; i >= 0; i--) {
    if (labs(noncollision[i].getypos()) > 5000) {
        noncollision.erase(noncollision.begin()+i);
    }
}

请注意永远不要将i更改为无符号变量(并且您的编译器可能会警告您这样做 - 即使用size_t代替 - 如果c.numparticles有明智之处type)因为如果你这样做,你的循环将永远不会结束!

答案 1 :(得分:2)

  

然而,这个for循环只删除其中一个,完全忽略了另一个。

这是因为你要前后。当您的代码删除索引6处的项目时,之前在索引7处的项目现在位于索引6处。但是,循环将在i++之后跳过索引6,认为它已经处理了它。

如果你回到前面,问题将得到解决:

for (int i = c.numparticles-1; i >= 0; i--)
{
    if ( labs((noncollision[i].getypos())) > 5000 )
    {
        noncollision.erase (noncollision.begin()+i);
    }
}

答案 2 :(得分:1)

看起来你正在遭受“无效的Iterator”综合症,尽管在这种情况下它是问题的索引。

你想要擦除的2个元素是否相邻?

问题是从向量中删除元素会导致剩余的基础元素被复制到新位置(除非删除最后一个元素),并且向量中的元素数量减少了一个。

由于您正在使用索引到向量中,因此您不会违反第一个问题(即迭代器失效),但是:

  • 你永远不会在你刚删除的元素之后立即检查元素
  • 您的索引将溢出向量的末尾(未定义的行为)

修改你在同一循环中检查的任何序列是个坏主意。看一下 remove_if以获得更好的方式。这个算法将所有匹配元素放在向量的末尾,并返回一个迭代器到第一个被移动的迭代器,允许你安全地一次性删除它们。