I recently finished fixing a bug in the following function, and the answer surprised me. I have the following function (written as it was before I found the bug):
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);
}
}
}
This function finds all Item
objects in the 'items' vector that has a certain position, removes them from 'items', and puts them in 'vect'. Later, a function named putItemsAt
does the opposite, and adds items to 'items'. The first time through, getItemsAt
works fine. After putItemsAt
is called, though, the for loop in getItemsAt
will run off the end of 'items'. 'it' will point at an invalid Item
pointer, and getPosition()
segfaults. On a hunch, I changed it!=items.end()
to it<items.end()
, and it worked. Can anyone tell me why? Looking around SO suggests it might involve erase
invalidating the iterator, but it still doesn't make sense why it would work the first time through.
I'm also curious because I plan to change 'items' from a vector to a list, since list's erase is more efficient. I know I'd have to use !=
for a list, as it doesn't have a <
operator. Would I run into the same problem using a list?