2010-12-21 15 views
5

Estoy consiguiendo ese error con este código:Vector iterator no se puede referenciar?

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

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

¿Cuál podría ser el problema?

evento cronometrado a veces empuja una nueva en cuando su devolución de llamada, que podrían hacerlo

Gracias

+0

¿Cuál es el texto de error exacto? – Tom

+0

Expresión: iterador de vector no referenciable (error de aserción de depuración) – jmasterx

+2

¿El destructor 'timedEventCallback' o' AguiTimedEvent' modifica directa o indirectamente 'timedEvents'? –

Respuesta

7

Si realiza un bucle a través de un vector y la función de devolución de llamada hace que se agregue el vector, todos los iteradores en el vector pueden invalidarse incluyendo la variable de bucle it.

En este caso (donde la devolución de llamada modifica el vector) probablemente sea mejor utilizar un índice como variable de bucle.

Probablemente necesite hacer un análisis exhaustivo del diseño para asegurarse de que no va a crear bucles infinitos inesperados.

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

Entonces, ¿cómo borraré un elemento vencido? – jmasterx

+0

@Milo: ver edición. –

1

Editar:

evento cronometrado a veces empuja un nuevo uno de cada cuando se llama a su devolución de llamada, que podría hacerlo

Sí, eso es muy definitivamente inseguro. Si el vector tiene que cambiar el tamaño, todos sus punteros e iteradores se invalidarán. Nunca se debe insertar en el medio de un vector mientras se está iterando, que causará la muerte. Tal vez si convirtieras a un bucle numérico for, el problema estaría resuelto.

+0

Puedo ser tonto, pero ¿por qué no es seguro? 'it' se reemplaza con el valor de retorno de' erase' y '.end()' se recalcula en cada iteración de bucle. –

+0

@ user470379: Puedo leer el comentario que pones en la otra respuesta, ya sabes. – Puppy

+1

No si se elimina, como acaba de hacer;) – user470379

3

evento cronometrado a veces empuja una nueva en su devolución de llamada cuando se llama, que podrían hacerlo

Sí.

Si agrega un elemento al vector, it puede ser invalidado.

+0

¿Cómo puedo solucionar esto? – jmasterx

+0

@Milo: Eso dependerá de lo que quiera que sean los resultados. ¿Dónde se agregan los nuevos elementos en el vector? ¿Quieres iterar sobre ellos también? Es sospechoso agregar elementos a un contenedor durante un pase de eliminación. –

+0

@Milo, inserte el elemento en un vector temporal y combínelo en el primario cuando termine el ciclo. –

1

Suena como un trabajo para std::remove_copy_if y std::remove_if: (no he probado esto, pero hay que darle la idea ...)

#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()); 

Tenga en cuenta que esta solución requiere tiempo lineal, mientras que el suyo toma tiempo cuadrádico Esto también elimina limpiamente el problema que las devoluciones de llamada podrían agregar al nuevo vector eliminando las decisiones sobre eso hasta después de que ya se haya realizado la eliminación del vector. Por otro lado, verifica el indicador expired dos veces, por lo que si se trata de una operación compleja, esto podría ser más lento.

Cuestiones relacionadas