C++ 如何擦除和删除指向存储在向量中的对象的指针?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/991335/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to erase & delete pointers to objects stored in a vector?
提问by Tony R
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。
回答by
You need to be careful because erase()
will invalidate existing iterators. However, it will return a new valid iterator you can use:
您需要小心,因为erase()
会使现有的迭代器无效。但是,它将返回一个您可以使用的新的有效迭代器:
for ( it = Entities.begin(); it != Entities.end(); ) {
if( (*it)->getXPos() > 1.5f ) {
delete * it;
it = Entities.erase(it);
}
else {
++it;
}
}
回答by rlbond
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. 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.
现在我知道你在想什么了。您认为其他一些答案较短。但是,(1) 这种方法通常编译成更快的代码——试着比较它,(2) 这是“正确的”STL 方式,(3) 出现愚蠢错误的机会更少,(4) 更容易阅读一旦你可以阅读 STL 代码。非常值得学习 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). Neil's code above, in contrast, is O(n^2), since the search is O(n) and removal is O(n).
另一个重要的一点是,通过直到操作结束才擦除元素,元素不需要被打乱。GMan 建议使用列表来避免这种情况,但是使用这种方法,整个操作是 O(n)。相比之下,Neil 的代码是 O(n^2),因为搜索是 O(n),移除是 O(n)。
回答by lhahne
if((*it)->getXPos() > 1.5f)
{
delete *it;
it = Entities.erase(it);
}
回答by Dolphin
The main problem is that most stl container iterators do not support adding or removing elements to the container. 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 容器迭代器不支持向容器添加或删除元素。有些会使所有迭代器无效,有些只会使指向已删除项的迭代器无效。在您对每个容器的工作方式有了更好的了解之前,您必须仔细阅读有关您可以对容器做什么和不可以做什么的文档。
stl containers don't enforce a particular implementation, but a vector is usually implemented by an array under the hood. 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.
stl 容器不强制执行特定的实现,但向量通常由引擎盖下的数组实现。如果您在开头删除一个元素,则所有其他项目都将被移动。如果您有一个指向其他项目之一的迭代器,它现在可能指向旧元素之后的元素。如果添加一个项目,则可能需要调整数组的大小,因此会创建一个新数组,将旧内容复制过来,现在您的迭代器指向旧版本的向量,这很糟糕。
For your problem it should be safe to iterate through the vector backwardsand 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);
}
}
回答by i_am_jorf
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. See here.
std::vector::erase() 返回一个迭代器,您应该使用它来替换您正在使用的迭代器。见这里。