0

我有一个简单的 for 循环:

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

noncollision类的向量在哪里particle。在此特定示例中,应擦除大于 5000noncollision的任何内容。ypos我一直在使用noncollision6 的大小,其中 2的大小ypos远远大于 5000。但是,这个 for 循环只是擦除其中一个,完全忽略另一个。我的怀疑是因为noncollision是类的向量,所以这些类受到某种保护,或者导致数组函数的行为不同?这是我对noncollision和 的声明particle

vector<particle> noncollision;

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

谁能解释为什么会发生这种情况,以及如何纠正它?我是否需要为班级设置一个“擦除功能” particle

4

3 回答 3

4

如果你有两个相邻的候选元素(比如 ati=5i=6),那么你跳过第二个,因为你刚刚删除了一个 at i=5... 然后第二个变成了 i=5的,但是你递增i以进入i=6下一个循环。

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

通常你会使用实际的迭代器(而不是 counter 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) 使用 alist<>而不是 avector<>如果从中间删除是一种常见的操作。


或者,如果你很懒惰,你也可以颠倒你的迭代顺序,i在这种特定情况下保持你的计数器的神圣性:

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

请注意不要更改i为无符号变量(并且您的编译器可能会警告您这样做 - 即改为使用size_t- ifc.numparticles具有合理的类型),因为如果您这样做,您的循环将永远不会结束!

于 2013-08-06T14:37:12.673 回答
2

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

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

如果您从后到前,问题将得到解决:

for (int i = c.numparticles-1; i >= 0; i--)
{
    if ( labs((noncollision[i].getypos())) > 5000 )
    {
        noncollision.erase (noncollision.begin()+i);
    }
}
于 2013-08-06T14:37:20.157 回答
1

看起来您正遭受“无效迭代器”综合症的困扰,尽管在这种情况下,问题出在索引上。

您要删除的 2 个元素是否彼此相邻?

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

由于您使用的是对向量的索引,因此您不会遇到第一个问题(即迭代器无效),但是:

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

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

于 2013-08-06T14:46:17.797 回答