web-dev-qa-db-ja.com

ベクターに保存されているオブジェクトへのポインターを消去および削除する方法は?

動的にインスタンス化された多くのオブジェクトへのポインターを格納するベクターがあり、ベクターを反復処理して特定の要素を削除しようとしています(ベクターから削除してオブジェクトを破棄します)が、問題があります。これは次のようなものです。

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

EntityオブジェクトのいずれかがxPos> 1.5に達すると、プログラムはアサーションエラーでクラッシュします...誰が私が間違っているのか知っていますか?

VC++ 2008を使用しています。

30
Tony R

erase() は既存のイテレータを無効にするため、注意する必要があります。ただし、使用できる新しい有効なイテレータが返されます。

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

これを行う「正しい」方法は、アルゴリズムを使用することです。

#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)愚かなエラーの可能性は低く、(4)読みやすいです一度STLコードを読むことができます。 STLプログラミングを学ぶ価値は十分にあります。スコット・マイヤーのすばらしい本「Effective STL」をチェックすることをお勧めします。

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

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

ベクトルを変更すると、すべての未処理のイテレーターは無効になります。つまり、ベクトルを繰り返し処理している間は、ベクトルを変更できません。それがメモリに何をするかを考えてみてください。その理由がわかります。あなたのアサートは「無効なイテレータ」アサートだと思います。

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

0
i_am_jorf

主な問題は、ほとんどのstlコンテナイテレータがコンテナへの要素の追加または削除をサポートしていないことです。すべてのイテレータを無効にするものもあれば、削除されたアイテムを指すイテレータのみを無効にするものもあります。各コンテナがどのように機能するかをよく理解するまで、コンテナに対してできることとできないことに関するドキュメントを注意深く読む必要があります。

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

あなたの問題のために、ベクトルbackwardsを繰り返し処理し、行くにつれて要素を削除しても安全であるはずです。また、後で削除するアイテムを移動する必要がないため、わずかに高速になります。

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
Dolphin