删除主要循环内的向量并添加元素

时间:2012-06-15 16:12:09

标签: c++ vector openframeworks

之前我搜索过但找不到任何答案。我对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 );
    }
}

7 个答案:

答案 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()。 不只是删除你的指向粒子的指针,而不是创建对象?