我有一个向量存储指向动态实例化的许多对象的指针,我正在尝试迭代向量并删除某些元素(从向量中移除并销毁对象),但我遇到了麻烦。这是它的样子:
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&gt; 1.5时,程序崩溃并出现断言错误... 谁知道我做错了什么?
我正在使用VC ++ 2008。
答案 0 :(得分:40)
您需要小心,因为erase()
将使现有迭代器无效。但是,它将返回一个可以使用的新的有效迭代器:
for ( it = Entities.begin(); it != Entities.end(); ) {
if( (*it)->getXPos() > 1.5f ) {
delete * it;
it = Entities.erase(it);
}
else {
++it;
}
}
答案 1 :(得分:9)
“正确”的方法是使用算法:
#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());
现在我知道你在想什么。你认为其他一些答案更短。 但是,(1)这种方法通常编译为更快的代码 - 尝试比较它,(2)这是“正确的”STL方式,(3)傻错误的机会较少,(4)它更容易阅读一旦你可以阅读STL代码。非常值得学习STL编程,我建议你查看Scott Meyer的伟大着作“Effective STL”,它有很多关于这类东西的STL技巧。
另一个重要的一点是,通过在操作结束之前不擦除元素,不需要对元素进行混洗。 GMan建议使用列表来避免这种情况,但使用这种方法,整个操作都是O(n)。相反,上面的Neil代码是O(n ^ 2),因为搜索是O(n),删除是O(n)。
答案 2 :(得分:2)
if((*it)->getXPos() > 1.5f)
{
delete *it;
it = Entities.erase(it);
}
答案 3 :(得分:0)
修改向量后,所有未完成的迭代器都将变为无效。换句话说,在迭代向量时无法修改向量。想想它对记忆的作用,你会明白为什么。我怀疑你的断言是一个“无效的迭代器”断言。
std :: vector :: erase()返回一个迭代器,您应该用它来替换您正在使用的迭代器。请参阅here。
答案 4 :(得分:0)
主要问题是大多数stl容器迭代器不支持向容器添加或删除元素。有些会使所有迭代器无效,有些只会使指向被删除项的迭代器无效。在您对每个容器的工作方式有了更好的了解之前,您必须小心阅读有关容器能够做什么和不能做什么的文档。
stl容器不强制执行特定的实现,但是向量通常由引擎盖下的数组实现。如果在开头删除元素,则移动所有其他项。如果你有一个指向其他项的迭代器,它现在可能指向旧元素之后的元素。如果你添加一个项目,那么可能需要调整数组的大小,这样就会创建一个新的数组,复制旧的东西,现在你的迭代器指向了旧版本的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);
}
}