Как стереть и удалить указатели на объекты, хранящиеся в векторе?

У меня есть вектор, который хранит указатели на многие объекты, создаваемые динамически, и я пытаюсь перебрать вектор и удалить определенные элементы (удалить из вектора и уничтожить объект), но у меня проблемы. Вот как это выглядит:

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

когда любой из объектов сущности получает xPos>1.5, программа аварийно завершает работу с ошибкой утверждения... Кто-нибудь знает что я делаю неправильно?

Я использую VC++ 2008.

5 ответов


вы должны быть осторожны, потому что erase() аннулирует существующие итераторы. Однако ir возвращает новый допустимый итератор, который можно использовать:

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

"правильный" способ сделать это, используя алгоритм:

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

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

после изменения вектора все выдающиеся итераторы становятся недействительными. Другими словами, вы не можете изменить вектор во время его итерации. Подумайте, что это делает с памятью, и вы поймете почему. Я подозреваю, что ваш assert является "недопустимым итератором" assert.

std::vector:: erase() возвращает итератор, который вы должны использовать для замены того, который вы использовали. См.здесь.


основная проблема заключается в том, что большинство итераторов контейнера stl не поддерживают добавление или удаление элементов в контейнер. Некоторые из них аннулируют все итераторы,некоторые - только итератор, указывающий на удаляемый элемент. Пока вы не почувствуете, как работает каждый из контейнеров, вам нужно будет внимательно прочитать документацию о том, что вы можете и не можете сделать с контейнером.

контейнеры stl не применяют определенную реализацию, но a vector обычно реализуется массивом под капотом. Если удалить элемент в начале, все остальные элементы будут перемещены. Если у вас был итератор, указывающий на один из других элементов, теперь он может указывать на элемент после старого элемента. Если вы добавляете элемент, массив может потребоваться изменить размер, поэтому создается новый массив, старый материал копируется, и теперь ваш итератор указывает на старую версию Вектора, что плохо.

для вашей проблемы должно быть безопасно повторите вектор назад и удалять элементы, как вы идете. Это также будет немного быстрее, так как вы не будете двигаться вокруг элементы, которые вы собираетесь удалить.

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