2

以前に検索しましたが、答えが見つかりませんでした。私はC++に少し慣れていないので、この質問がそれほど愚かではないことを願っています。

ベクトル内の要素を追加および削除しようとしています。私の場合は、すべてのパーティクルの大規模な更新または描画ループ中にパーティクルが入力されています。たとえば、いくつかのパーティクルが死んだために削除しますが、1つのパーティクルがオブジェクトと衝突したために他のいくつかのパーティクルを追加し、衝突点で小さなパーティクルバーストを表示したいとします。問題の根底に到達するために、この簡単なテストコードをデモファイルで作成しました。

問題は、パーティクルを削除して追加したため、イテレータポインタが無効になることだと思います。削除は機能しますが、ランダムなものをいくつか追加すると、nullポインターが返されます。以下のコードはやや冗長です。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 );
    }
}
4

7 に答える 7

6

に挿入するたびにvector、そのイテレータが無効になる可能性があります。これを行うことはできません:

if ( ofRandom(1) < .1 ) addSomeNewOnes();
it++

への呼び出し後は無効addSomeNewOnes()であるためです。it

への呼び出しによって返されたイテレータを使用できますvector::insertが、あなたの場合、それはコードを再設計することを意味します。

とにかく、コードが少しぎこちないので、これはやりたいことかもしれません。

于 2012-06-15T16:19:16.497 に答える
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--;
}

ここの情報に基づいて追加していない場合、STL のイテレータは安定しているため、前方に反復して同じ結果を得ることができるはずです。

于 2012-06-15T16:22:21.943 に答える
0
it = particles.erase(it);

消去された要素の次の要素の新しい位置を指す反復子を返します。消去されたものがたまたま最後のものである場合、それは を指しparticles.end()ます。it++on "end" はエラーです。

また、次の場合:

if ( ofRandom(1) < .1 ) addSomeNewOnes();

true に評価されaddSomeNewOnes()、他の人が言ったように呼び出され、イテレータも無効になります。

于 2012-06-15T16:21:54.360 に答える
0

基になるデータが変更されると、イテレータが無効になる場合があります。

ループから抜け出し、最初からやり直す必要があります。

drawParticles 関数全体をwhile(notDone)ループでラップし、ベクトルの変更が完了したら notDone を true に設定することで、これを実行できるはずです。

ルールに関する別の SO の質問は次のとおりです: Iterator invalidation rules

于 2012-06-15T16:17:25.843 に答える
0

イテレータの場所で挿入および削除していますか? その場合は、有効な戻りイテレータを挿入して消去すると、それらを使用できます。何かのようなもの:

std::vector<T>::iterator i = v.begin();
while ( i != v.end() ) {
    if ( someCondition ) {
        i = v.erase( i );
    } else {
        ++ i;
    }
}

は多かれ少なかれ標準的なイディオムです。

ランダムな挿入と削除の場合は、挿入または削除された要素の数、および挿入または削除が現在の位置の前または後ろにあったかどうかに従って更新するインデックスを操作する必要があります。

于 2012-06-15T16:26:06.997 に答える
0

無効化ルールを気にせずにこれを正しく機能させる簡単な方法があります。次の反復のために新しいベクトルを構築するだけです。

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 );
    }
}
于 2012-06-15T16:35:39.807 に答える
0

別のメモ:実装にメモリリークはありませんか? 使用している

vector<Particle*>  particles

また、 を使用しparticles.erase()ます。作成されたオブジェクトではなく、パーティクルへのポインタを削除するだけではありませんか?

于 2012-06-15T16:40:26.660 に答える