wake-up-neo.com

Wie kann man Zeiger auf in einem Vektor gespeicherte Objekte löschen und löschen?

Ich habe einen Vektor, der Zeiger auf viele Objekte speichert, die dynamisch instanziiert wurden, und ich versuche, den Vektor zu durchlaufen und bestimmte Elemente zu entfernen (vom Vektor entfernen und Objekt zerstören), aber ich habe Probleme. So sieht es aus:

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

Wenn eines der Entity-Objekte xPos> 1.5 erreicht, stürzt das Programm mit einem Assertionsfehler ab .. Weiß jemand, was ich falsch mache?

Ich verwende VC++ 2008.

29
Tony R

Sie müssen vorsichtig sein, da erase() vorhandene Iteratoren ungültig macht. Ir gibt jedoch einen neuen gültigen Iterator zurück, den Sie verwenden können:

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

Der "richtige" Weg, dies zu tun, ist die Verwendung eines Algorithmus:

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

Jetzt weiß ich, was du denkst. Sie denken, dass einige der anderen Antworten kürzer sind ... (1) Diese Methode kompiliert jedoch normalerweise zu schnellerem Code - vergleichen Sie sie, (2) dies ist der "richtige" STL-Weg, (3) eine geringere Chance für dumme Fehler, und (4) es ist einfacher zu lesen, sobald Sie STL-Code lesen können. Es lohnt sich, die STL-Programmierung zu lernen, und ich schlage vor, Sie schauen sich Scott Meyers großartiges Buch "Effective STL" an, das jede Menge STL-Tipps zu diesem Zeug enthält.

Ein weiterer wichtiger Punkt ist, dass Elemente nicht bis zum Ende des Vorgangs gelöscht werden und die Elemente nicht neu gemischt werden müssen. GMan schlug vor, eine Liste zu verwenden, um dies zu vermeiden, aber bei dieser Methode ist die gesamte Operation O (n). Neils Code oben ist dagegen O (n ^ 2), da die Suche O(n) und die Entfernung O (n) ist.

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

Wenn Sie den Vektor geändert haben, werden alle ausstehenden Iteratoren ungültig. Mit anderen Worten, Sie können den Vektor nicht ändern, während Sie ihn durchlaufen. Überlegen Sie, was das mit der Erinnerung bewirkt, und Sie werden sehen, warum. Ich vermute, dass Ihre Behauptung eine "ungültige" Iterator-Behauptung ist.

std :: vector :: erase () gibt einen Iterator zurück, den Sie verwenden sollten, um den von Ihnen verwendeten zu ersetzen. Siehe hier .

0
i_am_jorf

Das Hauptproblem ist, dass die meisten stl-Container-Iteratoren das Hinzufügen oder Entfernen von Elementen zum Container nicht unterstützen. Einige werden alle Iteratoren ungültig machen, andere machen nur einen Iterator ungültig, der auf ein Element zeigt, das entfernt wird. Bis Sie ein besseres Gefühl für die Funktionsweise der einzelnen Container erhalten, müssen Sie sorgfältig die Dokumentation lesen, was Sie mit einem Container tun können und was nicht.

stl-Container erzwingen keine bestimmte Implementierung, aber ein Vektor wird normalerweise von einem Array unter der Haube implementiert. Wenn Sie ein Element am Anfang entfernen, werden alle anderen Elemente verschoben. Wenn Sie einen Iterator hatten, der auf eines der anderen Elemente zeigte, zeigte es jetzt möglicherweise auf das Element nach dem alten Element. Wenn Sie ein Element hinzufügen, muss möglicherweise die Größe des Arrays geändert werden, sodass ein neues Array erstellt, der alte Inhalt kopiert wird und Ihr Iterator nun auf die alte Version des Vektors zeigt, was schlecht ist.

Für Ihr Problem sollte es sicher sein, den Vektor rückwärts zu durchlaufen und Elemente zu entfernen. Es wird auch etwas schneller sein, da Sie sich nicht um Elemente bewegen, die Sie später löschen werden.

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