C++ 向量迭代器不可解引用?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4504883/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 15:34:33  来源:igfitidea点击:

Vector iterator not dereferencable?

c++stlvectoriterator

提问by jmasterx

I'm getting that error with this code:

我收到此代码的错误:

for(std::vector<AguiTimedEvent*>::iterator it = timedEvents.begin();
    it != timedEvents.end();)
{
    if((*it)->expired())
    {
        (*it)->timedEventCallback();
        delete (*it);

        it = timedEvents.erase(it);
    }
    else
        it++;
}

What could be the problem?

可能是什么问题呢?

the timed event sometimes pushes a new one in when its callback is called, that might do it

定时事件有时会在调用其回调时推送一个新事件,这可能会这样做

Thanks

谢谢

回答by CB Bailey

If you are looping through a vector and the callback function causes the vector to be added to, then all iterators into the vector may be invalidated including the loop variable it.

如果您循环遍历一个向量并且回调函数导致该向量被添加到该向量中,那么所有进入该向量的迭代器可能会失效,包括循环变量it

In this case (where the callback modifies the vector) you are probably better off using an index as your loop variable.

在这种情况下(回调修改向量),您最好使用索引作为循环变量。

You probably need to do some thorough analysis of the design to make sure that you aren't going to create any unexpected infinite loops.

您可能需要对设计进行一些彻底的分析,以确保不会创建任何意外的无限循环。

E.g.

例如

for(std::vector<AguiTimedEvent*>::size_type n = 0;
    n < timedEvents.size();)
{
    if(timedEvents[n]->expired())
    {
        timedEvents[n]->timedEventCallback();
        delete timedEvents[n];

        timedEvents.erase(timedEvents.begin() + n);
    }
    else
        n++;
}

回答by James McNellis

the timed event sometimes pushes a new one in when its callback is called, that might do it

定时事件有时会在调用其回调时推送一个新事件,这可能会这样做

Yes.

是的。

If you add an element to the vector, itmay be invalidated.

如果向向量中添加元素,则it可能会失效。

回答by Puppy

Edit:

编辑:

the timed event sometimes pushes a new one in when its callback is called, that might do it

定时事件有时会在调用其回调时推送一个新事件,这可能会这样做

Yes, that is very definitely unsafe. If the vector has to resize, then all of it's pointers and iterators would be invalidated. You should never, ever insert into the middle of a vector whilst it's being iterated through, that will cause death. Maybe if you converted to a numerical for loop, the problem would be solved.

是的,这绝对是不安全的。如果向量必须调整大小,那么它的所有指针和迭代器都将失效。你永远不应该在迭代过程中插入向量的中间,这会导致死亡。也许如果您转换为数字 for 循环,问题就会解决。

回答by Billy ONeal

Sounds like a job for std::remove_copy_ifand std::remove_if: (Haven't tested this but it should give you the idea...)

像一个工作听起来std::remove_copy_ifstd::remove_if:(没有测试过这一点,但它应该给你的想法...)

#include <algorithm>
#include <functional>

//I'm sure you probably already have a delete functor lying around, right?
// No? Well here's one for you....
struct Deleter : public std::unary_function<AguiTimedEvent*, void>
{
    void operator()(AguiTimedEvent* toNuke)
    {
        delete toNuke;
    }
};

std::vector<AguiTimedEvent*> toRun;
std::remove_copy_if(timedEvents.begin(), timedEvents.end(), 
    std::back_inserter(toRun), std::not1(std::mem_fun(&AguiTimedEvent::expired)));
timedEvents.erase(std::remove_if(timedEvents.begin(), timedEvents.end(),
    std::mem_fun(&AguiTimedEvent::expired), timedEvents.end());
std::for_each(toRun.begin(), toRun.end(), 
    std::mem_fun(&AguiTimedEvent::timedEventCallback));
std::for_each(toRun.begin(), toRun.end(), Deleter());

Note that this solution takes linear time, while yours takes quadradic time. This also cleanly gets rid of the issue that the callbacks might add to the new vector by removing the decisions about that until after the removal from the vector has already been done. On the other hand, it checks the expiredflag twice, so if that's a complex operation this might be slower.

请注意,此解决方案需要线性时间,而您的解决方案需要二次时间。这也干净地消除了回调可能会添加到新向量的问题,方法是删除有关该问题的决定,直到从向量中删除之后。另一方面,它会检查expired标志两次,所以如果这是一个复杂的操作,这可能会更慢。