我最近在以下函数中修复了一个错误,答案让我感到惊讶.我有以下功能(在我发现错误之前编写):
void Level::getItemsAt(vector<item::Item>& vect, const Point& pt)
{
vector<itemPtr>::iterator it; // itemPtr is a typedef for a std::tr1::shared_ptr<item::Item>
for(it=items.begin(); it!=items.end(); ++it)
{
if((*it)->getPosition() == pt)
{
item::Item item(**it);
items.erase(it);
vect.push_back(item);
}
}
}
Run Code Online (Sandbox Code Playgroud)
此函数查找Item
"项目"向量中具有特定位置的所有对象,将其从"项目"中删除,并将它们放在"vect"中.之后,名为的函数putItemsAt
执行相反的操作,并将项添加到"项".第一次通过,getItemsAt
工作正常.putItemsAt
但是,在调用之后,for循环getItemsAt
将在'items'结束时运行.'它'将指向无效Item
指针和段错误getPosition()
.在预感中,我改变it!=items.end()
了it<items.end()
,并且它起作用了.谁能告诉我为什么?环顾SO表明它可能涉及erase
使迭代器无效,但它仍然没有意义,为什么它会第一次工作.
我也很好奇,因为我计划将"项目"从向量更改为列表,因为列表的擦除效率更高.我知道我必须使用!=
列表,因为它没有<
运算符.我会使用列表遇到同样的问题吗?
Rem*_*eau 10
当您调用erase()时,该迭代器将变为无效.因为那是你的循环迭代器,在使它失效后调用它上面的'++'运算符是未定义的行为.erase()返回一个新的有效迭代器,指向向量中的下一个项目.你需要在循环中使用那个新的迭代器,即:
void Level::getItemsAt(vector<item::Item>& vect, const Point& pt)
{
vector<itemPtr>::iterator it = items.begin();
while( it != items.end() )
{
if( (*it)->getPosition() == pt )
{
item::Item item(**it);
it = items.erase(it);
vect.push_back(item);
}
else
++it;
}
}
Run Code Online (Sandbox Code Playgroud)
您正在调用未定义的行为.向量的所有迭代器都被调用erase
该向量的事实无效.对于实现来说它做任何想做的事都是完全有效的.
当你打电话时items.erase(it);
,it
现在无效.为了符合标准,你现在必须假设it
已经死了.
您可以在下次调用时使用该无效迭代器调用未定义的行为vect.push_back
.
您可以使用循环it
的跟踪变量再次调用未定义的行为for
.
您可以使用以使代码有效std::remove_copy_if
.
class ItemIsAtPoint : std::unary_function<bool, item::Item>
{
Point pt;
public:
ItemIsAtPoint(const Point& inPt) : pt(inPt) {}
bool operator()(const item::Item* input)
{
return input->GetPosition() == pt;
}
};
void Level::getItemsAt(vector<item::Item>& vect, const Point& pt)
{
std::size_t oldSize = items.size();
std::remove_copy_if(items.begin(), items.end(), std::back_inserter(vect),
ItemIsAtPoint(pt));
items.resize(vect.size() - (items.size() - oldSize));
}
Run Code Online (Sandbox Code Playgroud)
如果你正在使用它boost::bind
,你可以使它更漂亮,但这是有效的.