简体   繁体   English

如何擦除和删除指向存储在向量中的对象的指针?

[英]How to erase & delete pointers to objects stored in a vector?

I have a vector that stores pointers to many objects instantiated dynamically, and I'm trying to iterate through the vector and remove certain elements (remove from vector and destroy object), but I'm having trouble.我有一个向量,它存储指向许多动态实例化的对象的指针,我正在尝试遍历向量并删除某些元素(从向量中删除并销毁对象),但是我遇到了麻烦。 Here's what it looks like:这是它的样子:

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

When any of the Entity objects get to xPos>1.5, the program crashes with an assertion error... Anyone know what I'm doing wrong?当任何实体对象达到 xPos>1.5 时,程序会因断言错误而崩溃……有人知道我做错了什么吗?

I'm using VC++ 2008.我正在使用 VC++ 2008。

You need to be careful because erase() will invalidate existing iterators.您需要小心,因为erase()会使现有的迭代器无效。 However, it will return a new valid iterator you can use:但是,它将返回一个您可以使用的新的有效迭代器:

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

The "right" way to do this is using an algorithm:做到这一点的“正确”方法是使用算法:

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

Now I know what you're thinking.现在我知道你在想什么了。 You're thinking that some of the other answers are shorter.您认为其他一些答案较短。 But, (1) this method typically compiles to faster code -- try comparing it, (2) this is the "proper" STL way, (3) there's less of a chance for silly errors, and (4) it's easier to read once you can read STL code.但是,(1) 这种方法通常编译成更快的代码——尝试比较它,(2) 这是“正确的”STL 方式,(3) 出现愚蠢错误的机会更少,(4) 更容易阅读一旦你可以阅读 STL 代码。 It's well worth learning STL programming, and I suggest you check Scott Meyer's great book "Effective STL" which has loads of STL tips on this kind of stuff.非常值得学习 STL 编程,我建议您查看 Scott Meyer 的好书“Effective STL”,其中包含有关此类内容的大量 STL 技巧。

Another important point is that by not erasing elements until the end of the operation, the elements don't need to be shuffled around.另一个重要的一点是,通过直到操作结束才擦除元素,元素不​​需要被打乱。 GMan was suggesting to use a list to avoid this, but using this method, the entire operation is O(n). GMan 建议使用列表来避免这种情况,但是使用这种方法,整个操作是 O(n)。 Neil's code above, in contrast, is O(n^2), since the search is O(n) and removal is O(n).相比之下,Neil 的代码是 O(n^2),因为搜索是 O(n),移除是 O(n)。

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

Once you modify the vector, all outstanding iterators become invalid.一旦修改了向量,所有未完成的迭代器都将失效。 In other words, you can't modify the vector while you are iterating through it.换句话说,您不能在迭代时修改向量。 Think about what that does to the memory and you'll see why.想想这对内存有什么影响,你就会明白为什么。 I suspect that your assert is an "invalid iterator" assert.我怀疑您的断言是“无效迭代器”断言。

std::vector::erase() returns an iterator that you should use to replace the one you were using. std::vector::erase() 返回一个迭代器,您应该使用它来替换您正在使用的迭代器。 See here .这里

The main problem is that most stl container iterators do not support adding or removing elements to the container.主要问题是大多数 stl 容器迭代器不支持向容器添加或删除元素。 Some will invalidate all iterators, some will only invalidate an iterator that is pointing at an item that is removed.有些会使所有迭代器无效,有些只会使指向已删除项的迭代器无效。 Until you get a better feeling for how each of the containers work, you will have to be careful to read the documentation on what you can and can't do to a container.在您对每个容器的工作方式有了更好的了解之前,您必须仔细阅读有关您可以对容器做什么和不可以做什么的文档。

stl containers don't enforce a particular implementation, but a vector is usually implemented by an array under the hood. stl 容器不强制执行特定的实现,但向量通常由引擎盖下的数组实现。 If you remove an element at the beginning, all the other items are moved.如果您在开头删除一个元素,则所有其他项目都将被移动。 If you had an iterator pointing to one of the other items it might now be pointing at the element after the old element.如果您有一个指向其他项目之一的迭代器,它现在可能指向旧元素之后的元素。 If you add an item, the array may need to be resized, so a new array is made, the old stuff copied over, and now your iterator is pointing to the old version of the vector, which is bad.如果您添加一个项目,则可能需要调整数组大小,因此会创建一个新数组,将旧内容复制过来,现在您的迭代器指向旧版本的向量,这很糟糕。

For your problem it should be safe to iterate through the vector backwards and remove elements as you go.对于您的问题,向后遍历向量并随时删除元素应该是安全的。 It will also be slightly faster, since you wont be moving around items that you are going to later delete.它也会稍微快一点,因为您不会在以后要删除的项目周围移动。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM