2008-10-13 12 views
6

Tengo un proyecto antiguo que se construyó usando Visual Studio 2003 y lo recompbé con vs2005 recientemente. Sin embargo, durante el tiempo de ejecución, me sale el siguiente error:iterador de lista no aumentable

iterador de la lista no incrementable

tracé el programa para esta función:

void InputQueue::update() 
{ 
    list<PCB>::iterator iter; 
    list<PCB>::iterator iterTemp; 
    for(iter = begin(); iter != end(); iter++) 
    { 
     if(iter->arrivalTime == 0) 
     {   
      ReadyQueue::getInstance()->add(*iter); 
      iterTemp = iter; 
      iter++; 
      erase(iterTemp); 
     } 
    } 
} 

No soy un experto en C++ y esto es lo más lejos como el depurador VS me consiguió. ¿Alguien podría explicarme cuál es el problema?

Gracias

Respuesta

9

en cuenta que si iter->arrivalTime == 0, entonces la lista iterador se incrementa dos veces: una vez antes de la retirada elemento, y una vez de nuevo al final del bucle.

Si el elemento que se va a eliminar es el último de la lista, obviamente no funcionará correctamente. Me atrevo a decir que nunca funcionó correctamente, incluso en VS2003, pero VS2005 te alerta sobre ello mejor. :-)

Recuerde, es un comportamiento indefinido repetir el pasado end(). Absolutamente puede suceder cualquier cosa, como un bloqueo del programa o (en este caso) un mensaje de error.

1

sólo voy a eludir unas pocas líneas de código para mostrar dónde está el problema:

for(iter = begin(); iter != end(); iter++) // *** 
    { 
     if(iter->arrivalTime == 0) 
     {      

       iter++; // *** 

     } 
    } 

En las dos líneas marcadas ***, se está incrementando el iterador. El problema es que en la segunda de las dos líneas, no verifica que no haya llegado al final del contenedor. Efectivamente, si ingresas al ciclo interno, estás incrementando dos veces, pero solo verificando si puedes incrementar una vez.

Una solución es comprobar si usted está en end() antes de hacer el segundo incremento, pero me parece que está intentando formar previamente la misma operación que yo estaba en my question a while ago que ver con elementos de filtrado de un recipiente (un mapa en ese caso, pero lo mismo aplica para la mayoría de los contenedores STL).

0

I beliebe Chris tiene razón. Sin embargo, otro problema podría deberse al hecho de que usted asigna al iterador. - ¿Se garantiza que los iteradores de lista sean asignables? Sin mirar el estándar, no lo creo porque la asignabilidad no se menciona en ninguna parte en la documentación SGI de los iteradores.

+0

Parece de http://www.sgi.com/tech/stl/Iterators.html que los iteradores de ida son asignables. Los iteradores de std :: list son iteradores bidireccionales (http://www.sgi.com/tech/stl/List.html, http://www.sgi.com/tech/stl/ReversibleContainer.html), y por lo tanto también iteradores hacia adelante :-) –

+0

Hmm, ¿esto es lo que quieren decir con "multi-pass"? Porque, de lo contrario, no se dice nada sobre la asignabilidad * del iterador * (¡a diferencia de su valor!). –

14

Me gustaría volver a escribir su bucle de ser como la siguiente:

while (iter != end()) 
{ 
    if (iter->arrivalTime == 0) 
    { 
    ReadyQueue::getInstance()->add(*iter); 
    iter = erase(iter); 
    } 
    else 
    { 
    ++iter; 
    } 
} 

Ahora se recorre correctamente a través de la lista de comprobación de todos los índices.

+0

No está incrementando el iterador en la primera parte de if –

+1

I am - iter = borrar (iter). La función de borrado devuelve el nuevo iterador después del que acaba de eliminarse. –

+0

Oh, no se preocupe. Esto no funciona con ciertos tipos de contenedores, importa –

0

Esto es solo una nota al margen, pero una importante.

Supongo que heredas de un std::ist<PCB>. Debo decir que heredar para reutilizar la funcionalidad no siempre me ha ido bien. Pero como también está 'heredando' el proyecto, no hay mucho que hacer al respecto ...

+0

La herencia de implementación, aunque no es ideal, puede ser perdonable solo si se trata de herencia privada. :-) –

0

Si obtiene el "iterador de lista incompatible" es probable que sea porque dentro de su "ReadyQueue :: getInstance() -> add (* iter); " está cambiando algo en * iter que hace que el algoritmo hash devuelva un valor diferente para borrar de lo que lo hizo durante la inserción.

0

¿Puedo sugerir un algoritmo más simple?

La función gratuita std::remove_if se puede utilizar para dividir la lista en 2, elementos que coinciden o no coinciden con el predicado (es decir, arrivalTime == 0). Devuelve el iterador separando los rangos. A continuación, puede llamar al ReadyQueue::getInstance()->add(subrange_begin, subrange_end)(tiene esa sobrecarga, ¿no?) y borrar el subrango posterior.

Solo un caso en el que puede usar algoritmos STL en lugar de escribir sus propios bucles.

1

La causa raíz es "list.erase()" cambiará el iterador. La escritura correcta de bucle "for":

for (list<CMessage*>::iterator it=que.begin(); it!=que.end(); ++it) 
    { 
    if(m_type == (*it)->m_type) 
    { 
     delete *it; 
     it=que.erase(it); //"list.erase()" will change the iterator!!! 
     if(it==que.end()) break; //Check again!!! 
     //still has side effect here. --it? 
    } 
    } 

Pero todavía tiene efectos secundarios, por lo que la solución mientras que Mark será la mejor.

Cuestiones relacionadas