之前我搜索过但找不到任何答案。我对c ++有些新意,所以希望这个问题不会太愚蠢。
我正在尝试添加和删除向量中的元素,在我的情况下,在大型更新期间填充粒子或在所有粒子上绘制循环。例如,移除一些粒子,因为它们已经死亡,但也添加了一些粒子,因为一个粒子与一个物体相撞,我想在碰撞点显示一个小粒子爆发。我在演示文件中制作了这个简单的测试代码,以找到问题的根源。
我认为问题是因为我删除并添加了粒子,迭代器指针变得无效。删除工作,但当我添加一些随机的,我得到一个空指针。下面的代码有点冗长,我知道我应该使用带有begin()和end()的迭代器,但我遇到了同样的问题,并且稍微使用了代码,尝试更多的javascript数组样式循环因为我更熟悉这一点。
void testApp::drawParticles()
{
int i=0;
int max = particles.size();
vector<Particle*>::iterator it = particles.begin();
while ( i < max ) {
Particle * p = particles[i];
if ( ofRandom(1) > .9 ) {
it = particles.erase(it);
max = particles.size();
} else {
ofSetColor(255, 255, 0);
ofCircle( p->x, p->y, p->z, 10);
if ( ofRandom(1) < .1 ) addSomeNewOnes();
i++;
it++;
}
}
}
void testApp::addSomeNewOnes()
{
int max = ofRandom(4);
for ( int i=0; i<max; i++ ) {
Particle * p = new Particle();
p->x = ofRandom( -ofGetWidth()/2, ofGetWidth()/2 );
p->y = ofRandom( -ofGetHeight()/2, ofGetHeight()/2 );
p->z = ofRandom( -ofGetHeight()/2, ofGetHeight()/2 );
particles.push_back( p );
}
}
答案 0 :(得分:6)
每次插入vector
时,它的迭代器都可能无效。你不能这样做:
if ( ofRandom(1) < .1 ) addSomeNewOnes();
it++
因为在致电addSomeNewOnes()
后,it
无效。
您可以使用调用vector::insert
返回的迭代器,但在您的情况下,这意味着重新设计代码。
无论如何,这是您可能想要做的事情,因为您的代码有点笨拙。
答案 1 :(得分:1)
你可以从最后循环,这应该允许你删除你的当前(因为你只是删除了结尾)并添加新的添加到最后:
Vector<Particle*>::iterator it = particles.end();
while (iter != particles.begin()) {
Particle * p = *iter;
if ( ofRandom(1) > .9 ) {
particles.erase(iter);
} else {
ofSetColor(255, 255, 0);
ofCircle( p->x, p->y, p->z, 10);
if ( ofRandom(1) < .1 ) addSomeNewOnes();
}
iter--;
}
如果您没有添加,基于信息here,STL中的迭代器是稳定的,因此您应该能够向前迭代并仍然能够获得相同的结果。
答案 2 :(得分:0)
在基础数据发生变化的某些情况下,迭代器会失效。
你必须打破循环并重新开始。
您应该可以通过将整个drawParticles函数包装在while(notDone)
循环中,并在完成修改向量时将notDone设置为true来实现此目的。
以下是规则的另一个问题:Iterator invalidation rules
答案 3 :(得分:0)
it = particles.erase(it);
将返回一个迭代器,指向删除后的元素的新位置。如果擦除的那个恰好是最后一个,它将指向particles.end()
。 “结束”上的it++
是错误。
另外,如果:
if ( ofRandom(1) < .1 ) addSomeNewOnes();
计算结果为true,并且正如其他人所说,addSomeNewOnes()
被调用,这也会使迭代器失效。
答案 4 :(得分:0)
您是否在迭代器的位置插入和删除?如果是这样, 插入和擦除返回有效的迭代器,你可以使用 那些。类似的东西:
std::vector<T>::iterator i = v.begin();
while ( i != v.end() ) {
if ( someCondition ) {
i = v.erase( i );
} else {
++ i;
}
}
或多或少是标准的习惯用语。
对于随机插入和删除,您必须使用索引 您根据插入或删除的元素数量进行更新,以及 插入或删除是在当前的前面还是后面 位置。
答案 5 :(得分:0)
有一种简单的方法可以正常工作而不必担心失效规则:只需为下一次迭代构建一个 new 向量。
typedef vector<Particle*> ParticleVector;
// modifies the particle vector passed in
void testApp::drawParticles(ParticleVector &particles)
{
ParticleVector next;
next.reserve(particles.size()); // seems like a reasonable guess
for (auto it = particles.begin(); it != particles.end(); ++it)
{
Particle *p = *it;
if (ofRandom(1) > .9) {
// don't copy to the next cycle
delete p;
} else {
// copy to the next cycle
next.push_back(p);
ofSetColor(255, 255, 0);
ofCircle(p->x, p->y, p->z, 10);
if (ofRandom(1) < .1)
addSomeNewOnesTo(next);
}
}
particles.swap(next);
}
注意当你不使用全局变量时,重构会更容易,顺便说一下。
void testApp::addSomeNewOnesTo(ParticleVector &particles)
{
int max = ofRandom(4);
for ( int i=0; i<max; i++ ) {
Particle * p = new Particle();
p->x = ofRandom( -ofGetWidth()/2, ofGetWidth()/2 );
p->y = ofRandom( -ofGetHeight()/2, ofGetHeight()/2 );
p->z = ofRandom( -ofGetHeight()/2, ofGetHeight()/2 );
particles.push_back( p );
}
}
答案 6 :(得分:0)
另一方面:您的实施中是否存在内存泄漏? 您正在使用
vector<Particle*> particles
并使用particles.erase()
。
不只是删除你的指向粒子的指针,而不是创建对象?