Вектор итератор не разыменовывается? - PullRequest
5 голосов
/ 22 декабря 2010

Я получаю эту ошибку с этим кодом:

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

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

В чем может быть проблема?

временное событие иногда выдвигает новое, когда вызывается его обратный вызов, чтоможет сделать это

Спасибо

Ответы [ 4 ]

7 голосов
/ 22 декабря 2010

Если вы перебираете вектор и функция обратного вызова вызывает добавление вектора, то все итераторы в векторе могут быть недействительными, включая переменную цикла it.

В этом случае (когда обратный вызов изменяет вектор) вам лучше использовать индекс в качестве переменной цикла.

Возможно, вам необходимо провести тщательный анализ проекта, чтобы убедиться, что вы не собираетесь создавать какие-либо неожиданные бесконечные циклы.

1008 * Е.Г. *

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++;
}
3 голосов
/ 22 декабря 2010

временное событие иногда вставляет новое при вызове его обратного вызова, что может сделать это

Да.

Если вы добавляете элемент в вектор, it может быть недействительным.

1 голос
/ 22 декабря 2010

Похоже на работу для std::remove_copy_if и std::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, поэтому, если это сложная операция, она может быть медленнее.

1 голос
/ 22 декабря 2010

Редактировать:

временное событие иногда вставляет новое, когда вызывается его обратный вызов, что может сделать это

Да, это совершенно определенно небезопасно.Если вектор должен изменить размер, то все его указатели и итераторы будут признаны недействительными.Никогда не вставляйте в середину вектора, пока он повторяется, это приведет к смерти.Возможно, если вы преобразуете в числовой цикл for, проблема будет решена.

...