2012-06-15 58 views
2

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

回答

6

每次你在插入到vector,迭代器將它的潛在失效。你可以這樣做:

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

因爲調用addSomeNewOnes()後,it無效。

您可以使用通過調用返回的迭代器vector::insert,但在你的情況下,這將意味着重新設計你的代碼。

無論如何,這是你可能想要做的事情,因爲你的代碼有點笨拙。

+1

我結束了剛剛重做我的代碼,並在循環完成後做我的插入,並沒有攪亂主循環迭代器 –

0

當底層數據發生變化時,迭代器在某些情況下會失效。

您必須跳出循環並重新開始。

您應該可以通過將您的整個drawParticles函數包裝在while(notDone)循環中,並在完成修改矢量後將notDone設置爲true。

這裏的規矩另一個SO問題:Iterator invalidation rules

0
it = particles.erase(it); 

將返回一個迭代器指向元素的新位置後面的一個擦除。如果擦除的是最後一個,它將指向particles.end()。關於「結束」的it++是錯誤。

而且,如果:

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

計算結果爲真,addSomeNewOnes()被調用,正如其他人所說,這也將無效的迭代器。

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中的迭代器是穩定的,所以你應該能夠迭代前進,仍然能夠達到相同的結果。

+0

,一個不適合我...當我運行這個我仍然得到一個空指針。我的編譯器也抱怨'while(* iter)'不能轉換爲bool。所以如果我使用'while(* iter!= particles.begin()'我回到了老問題 –

+0

我糾正了循環條件(它應該是''iter!= particles.begin()''而不是' * iter!= particles.begin()'')。你現在可以嘗試嗎?另外,如果你可以將編譯器錯誤/輸出添加到[gist](https://gist.github.com/)或pastebin某處 – scorpiodawg

0

您是否在迭代器的位置插入和刪除?如果是這樣, 插入和擦除將是有效的返回迭代器,你可以使用 那些。例如:

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

是一個或多或少的標準習慣用法。

對於隨機的插入和缺失,您有指數法工作,你根據插入或刪除元素的數目更新哪些 ,和 插入或缺失是否在前面或當前 位置的後方。

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); 
    } 
} 
0

另一個說明:在你的實現中沒有內存泄漏嗎? 您正在使用

vector<Particle*> particles 

,並使用particles.erase()。 不只是刪除指向粒子的指針,而不是創建的對象?

+0

是的,我忽略它,因爲我只是在寫測試 –

相關問題