Safe way to continuously erase from a std::vector?

2019-07-11 04:02发布

问题:

I thought the following code would work but it crashes when the target widget is at the end of the vector.

for(std::vector<AguiWidget*>::iterator it = children.begin();
        it != children.end(); ++it)
    {
        if((*it) == widget)
            it = children.erase(it);
    }

I want it to go through and delete any instance it finds of widget. I understand this method is N^2 but since this is event driven it is fine. I just don't know why this should fail. When it does, 'it' == widget.

Thanks

回答1:

You can use the erase-remove idiom to erase all elements that are equal to widget.

children.erase(remove(children.begin(), children.end(), widget), children.end());


回答2:

You should probably stick to lists if you want to use erase like that. But the problem is that you invalidate your iterator, and then try to increment it. Try this instead.

for(std::vector<AguiWidget*>::iterator it = children.begin();
    it != children.end();)
{
    if(*it == widget)
        children.erase(it++);
    else
        ++it;
}

Notice that I'm not incrementing the iterator inside the for-loop statement.



回答3:

You do realize you're comparing pointers, not dereferences, right?

Can you tell us what happens if you use the remove-erase idiom? This is going to be fast(er than your code) and correct:

children.erase(std::remove_if(children.begin(), children.end(),
                              std::bind1st(std::equal_to<AguiWidget*>(),
                                           widget)));

Also, don't forget to delete the pointers first.

for_each_if(children.begin(), children.end(),
            std::bind1st(std::equal_to<AguiWidget*>(), widget),
            Delete());

Of course, you'll have to be sure no two pointers point at the same object.



回答4:

To complement Blastfurnace's answer, you can also do so with a simple for loop, if you do it backward.

for (widgets::reverse_iterator it = children.rbegin(), end = children.rend();
     it != end; ++it)
{
  if (*it == widget) { children.erase(it.base()); }
}