2009-06-13 13 views
25

私は、動的にインスタンス化された多くのオブジェクトへのポインタを格納するベクトルを持っており、ベクトルを反復処理して特定の要素を削除しようとしています(ベクトルから削除してオブジェクトを破棄します)。エンティティオブジェクトのいずれかがアサーションエラーでXPOS> 1.5、プログラムがクラッシュを取得する場合ベクトルに格納されているオブジェクトへのポインタの消去と削除方法は?

vector<Entity*> Entities; 
    /* Fill vector here */ 
    vector<Entity*>::iterator it; 
    for(it=Entities.begin(); it!=Entities.end(); it++) 
     if((*it)->getXPos() > 1.5f) 
      Entities.erase(it); 

... 誰もが私が間違ってやっているか知っている:ここではそれがどのように見えるのか?

私が使用しているVC++

+0

ご使用の言語/環境に質問にタグを付けて、メインページから何を使用しているかを確認してください(さらに多くのビューが表示されます)。 – Zifre

+0

しかし、あなたはコードの残りの部分でベクトルを使って何をしているのか分かりません!一般に、ベクトルは最初の選択コンテ​​ナでなければならず、他のものは等しい。 –

+2

一般に、手書きループにはSTLアルゴリズムを使用することをお勧めします。 – rlbond

答えて

37

erase()は既存のイテレータを無効にするため、注意が必要です。しかし、IRはあなたが使用できる新しい有効なイテレータを返します。これはアルゴリズムを使用して行うには「正しい」方法

for (it = Entities.begin(); it != Entities.end();) 
    if((*it)->getXPos() > 1.5f) 
     delete * it; 
     it = Entities.erase(it); 
    } 
    else { 
     ++it; 
    } 
} 
+0

これとKeand64の答えの違いは何ですか? vector :: erase()は、オブジェクトのデストラクタを呼び出すことを要求します。したがって、 "delete * it;"必要? –

+4

ポインタにはデストラクタがありません。ベクトル内のもののデストラクタは、エンティティ値のコレクションである場合にのみ呼び出されます。したがって、メモリリークを避けるには、削除の呼び出しが不可欠です。 –

+1

@Gman私はあなたがイテレータを参照しているのではなく、ポインタを参照していると思います。 –

0

あなたはベクトルを変更すると、2008年には、すべての未処理のイテレータが無効になります。言い換えれば、反復処理中にベクターを変更することはできません。それが記憶に何をするか考えてみると、その理由がわかります。私はあなたの主張が "無効な反復子"の主張だと思う。

std :: vector :: erase()は、使用していたイテレータを置き換えるために使用するイテレータを返します。 hereを参照してください。

+0

消去は、実際には、消去された項目と要素を指すイテレータを無効にします。下位要素を指すイテレータは無効化されません。 – Dolphin

2
if((*it)->getXPos() > 1.5f) 
{ 
    delete *it; 
    it = Entities.erase(it); 
} 
+2

erase()から返されたイテレータが消去後にインクリメントされるため、これは間違っています。 –

8

を:

#include <algorithm> 
#include <functional> 

// this is a function object to delete a pointer matching our criteria. 
struct entity_deleter 
{ 
    void operator()(Entity*& e) // important to take pointer by reference! 
    { 
     if (e->GetXPos() > 1.5f) 
     { 
      delete e; 
      e = NULL; 
     } 
} 

// now, apply entity_deleter to each element, remove the elements that were deleted, 
// and erase them from the vector 
for_each(Entities.begin(), Entities.end(), entity_deleter()); 
vector<Entity*>::iterator new_end = remove(Entities.begin(), Entities.end(), static_cast<Entity*>(NULL)); 
Entities.erase(new_end, Entities.end()); 

今、私はあなたが考えているか知っています。他の回答のほうが短いと思っています。 しかし、(1)このメソッドは通常、より速いコードにコンパイルします - それを比較しようとします、(2)これは適切なSTL方法です、(3)愚かなエラーの可能性が少なく、 STLコードを読むことができれば読んでください。 STLのプログラミングを学ぶ価値はありますが、Scott Meyerの素晴らしい本「Effective STL」にこの種のSTLヒントがあります。

もう1つ重要なことは、操作が終了するまで要素を消去しないことで、要素をシャッフルする必要がないことです。 GManはこれを避けるためにリストを使用することを提案していましたが、この方法を使用すると、操作全体がO(n)になります。対照的に、上記のNeilのコードは、検索がO(n)であり、削除がO(n)であるため、O(n^2)です。

+0

私はやっていないし、ポインタを削除する必要がない場合は、上のコードがあると書いています。時には、共有所有権を持つポインタのコンテナが必要な場合もあります。 – rlbond

+4

明示的なループIMHOよりも明らかにあまり明確ではありません。 –

+0

さらにコードも増えています! –

0

主な問題は、ほとんどのstlコンテナイテレータは、コンテナへの要素の追加または削除をサポートしていないことです。あるイテレータはすべてのイテレータを無効にするものと、削除されたアイテムを指すイテレータのみを無効にするものがあります。それぞれのコンテナがどのように機能するかについてのより良い気持ちが得られるまで、コンテナにできることとできないことに関するドキュメントを慎重に読む必要があります。

stlコンテナは特定の実装を強制しませんが、通常、ベクトルはフードの下の配列によって実装されます。最初に要素を削除すると、他のすべての要素が移動します。イテレータが他のアイテムの1つを指している場合、古い要素の後の要素を指している可能性があります。アイテムを追加する場合は、配列のサイズを変更する必要があります。新しい配列が作成され、古いものがコピーされ、イテレータが古いバージョンのベクターを指しています。

問題がある場合は、ベクトルを逆方向に繰り返して、の要素を削除しても問題はありません。後で削除する予定の項目を移動しないので、少し速くなります。

vector<Entity*> Entities; 
/* Fill vector here */ 
vector<Entity*>::iterator it; 
for(it=Entities.end(); it!=Entities.begin();){ 
    --it; 
    if(*(*it) > 1.5f){ 
    delete *it; 
    it=Entities.erase(it); 
    } 
} 
+0

逆方向反復は巧妙ですが、あなたは2つの問題に遭遇します。 最初に、vector :: erase()はすべてのイテレータを無効にするので、上のコードでは無効なイテレータが使用されています。 しかし、より重大な問題は、vector :: erase()がreverse_iteratorを受け入れないことです!上記で書いたコードはコンパイルすべきではありません。 – rlbond

+0

hmmm、reverse_iteratorを使用しない消去が問題になる可能性があります:) しかし、eraseは要素が消去される前に要素を指すイテレータを無効にしません。いずれにせよ、コンパイルとワーキングコードで修正されました。 – Dolphin

関連する問題