web-dev-qa-db-fra.com

Comment effacer et supprimer des pointeurs sur des objets stockés dans un vecteur?

J'ai un vecteur qui stocke les pointeurs vers de nombreux objets instanciés de manière dynamique, et j'essaye de parcourir le vecteur et de supprimer certains éléments (supprimer du vecteur et détruire l'objet), mais j'ai des problèmes. Voici à quoi ça ressemble:

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

Lorsque l'un des objets Entity parvient à xPos> 1.5, le programme se bloque avec une erreur d'assertion .... Tout le monde sait ce que je fais mal?

J'utilise VC++ 2008.

29
Tony R

Soyez prudent car erase() invalidera les itérateurs existants. Cependant, ir renvoie un nouvel itérateur valide, que vous pouvez utiliser:

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

La "bonne" façon de faire utilise un algorithme:

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

Maintenant je sais ce que tu penses. Vous pensez que certaines des autres réponses sont plus courtes… .. Mais, (1) cette méthode est généralement compilée en un code plus rapide - essayez de la comparer, (2) c'est la méthode «appropriée» de la STL, (3) il y a Une chance moindre d’erreurs stupides, et (4) c’est plus facile à lire une fois que vous pouvez lire le code STL. Il vaut la peine d'apprendre la programmation STL et je vous suggère de consulter l'excellent livre de Scott Meyer, "Effective STL", qui contient de nombreux conseils sur ce type de choses.

Un autre point important est qu'en évitant d'effacer des éléments jusqu'à la fin de l'opération, il n'est pas nécessaire de les mélanger. GMan a suggéré d'utiliser une liste pour éviter cela, mais en utilisant cette méthode, l'opération entière est O (n). Le code de Neil ci-dessus, en revanche, est O (n ^ 2), puisque la recherche est O(n) et la suppression est O (n).

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

Une fois que vous avez modifié le vecteur, tous les itérateurs en attente deviennent invalides. En d'autres termes, vous ne pouvez pas modifier le vecteur pendant que vous le parcourez. Pensez à ce que cela fait à la mémoire et vous verrez pourquoi. Je suppose que votre assertion est une assertion "itérateur invalide".

std :: vector :: erase () retourne un itérateur que vous devriez utiliser pour remplacer celui que vous utilisiez. Voir ici .

0
i_am_jorf

Le problème principal est que la plupart des itérateurs de conteneur stl ne prennent pas en charge l'ajout ou la suppression d'éléments dans le conteneur. Certains invalideront tous les itérateurs, d'autres n'invalideront qu'un itérateur qui pointe sur un élément supprimé. En attendant de mieux comprendre le fonctionnement de chacun des conteneurs, vous devrez lire attentivement la documentation sur ce que vous pouvez et ne pouvez pas faire avec un conteneur.

les conteneurs stl n'appliquent pas d'implémentation particulière, mais un vecteur est généralement implémenté par un tableau sous le capot. Si vous supprimez un élément au début, tous les autres éléments sont déplacés. Si un itérateur pointait vers l'un des autres éléments, il pourrait maintenant être dirigé vers l'élément après l'ancien. Si vous ajoutez un élément, le tableau devra peut-être être redimensionné. Un nouveau tableau est donc créé, l'ancien élément copié, et votre itérateur pointe maintenant sur l'ancienne version du vecteur, qui est incorrecte.

Pour votre problème, il devrait être prudent de parcourir le vecteur en arrière et de supprimer des éléments au fur et à mesure. Il sera également légèrement plus rapide, car vous ne déplacerez pas d'éléments que vous allez supprimer ultérieurement.

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