【问题标题】:Vector iterator not dereferencable?向量迭代器不可解引用?
【发布时间】:2011-05-29 03:21:51
【问题描述】:

我收到此代码的错误:

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

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

可能是什么问题?

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

谢谢

【问题讨论】:

  • 确切的错误文本是什么?
  • 表达式:向量迭代器不可解引用(调试断言失败)
  • timedEventCallbackAguiTimedEvent的析构函数是否直接或间接修改timedEvents
  • 定时事件有时会在调用其回调时推送一个新事件,这可能会这样做
  • 如果您在迭代时修改集合(使用回调),则无法保证迭代器在之后仍然有效。那可能是你的问题。

标签: c++ stl vector iterator


【解决方案1】:

编辑:

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

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

【讨论】:

  • 我可能很笨,但为什么不安全? iterase 的返回值替换,.end() 在每次循环迭代中重新计算。
  • @user470379:你知道,我可以阅读你对另一个答案的评论。
  • 如果它被删除则不会,就像刚刚那样;)
  • @user470379:可以肯定的是,由于拥有超过 10k 的代表,我实际上可以查看已删除的答案及其相关的 cmets。至于 erase() 安全性,那么我同意你的看法——我也看不出它是如何无效的。但是,在 OP 的代码中绝对没有其他错误来源,这是一个非常简单的循环,所以除非你想让我断定他的 STL 实现或编译器有错误,否则只有一件事要看。
  • 仅仅因为你有超过 10k 的代表并不意味着每个人都在看答案/cmets。但是,既然您已经完全重写了答案,就不需要原来的评论了。
【解决方案2】:

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

是的。

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

【讨论】:

  • @Milo:这取决于你想要的结果。向量中添加的新元素在哪里?你也想迭代它们吗?怀疑是在移除过程中向容器添加元素。
  • @Milo,将元素推送到一个临时向量上,并在循环完成后将其合并到主向量中。
  • @Mark, @Milo: 或者使用deque 而不是vector。附加到 deque 永远不会使早期的迭代器失效。
【解决方案3】:

如果您正在循环一个向量并且回调函数导致该向量被添加到该向量中,则该向量中的所有迭代器都可能无效,包括循环变量it

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

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

例如

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++;
}

【讨论】:

  • 那我将如何擦除一个过期的元素呢?
【解决方案4】:

听起来像是 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());

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-20
    • 2016-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多