2008-10-13 10 views
6

J'ai un ancien projet qui a été construit en utilisant Visual Studio 2003 et je l'ai récemment recompilé avec vs2005. Cependant, lors de l'exécution, je reçois l'erreur suivante:list itérateur non incrémentable

liste iterator pas Incrementable

Je traçais le programme à cette fonction:

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

Je ne suis pas un expert en C++ et c'est aussi loin comme le débogueur VS m'a eu. Quelqu'un pourrait-il m'expliquer quel est le problème?

Merci

Répondre

9

Notez que si iter->arrivalTime == 0, la liste se iterator incrémenté deux fois: une fois avant le retrait de l'élément, et encore une fois à la fin de la boucle.

Si l'élément à supprimer est le dernier élément de la liste, cela ne fonctionnera évidemment pas correctement. J'ose dire que cela n'a jamais fonctionné correctement même dans VS2003, mais VS2005 vous en avertit mieux. :-)

Rappelez-vous, il est un comportement indéfini pour itérer passé end(). Absolument tout peut arriver, comme un crash de programme, ou (dans ce cas) un message d'erreur.

1

Je vais juste elide quelques lignes de code pour montrer où est le problème:

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

       iter++; // *** 

     } 
    } 

Sur les deux lignes marquées ***, vous incrémentez le iterator. Le problème est que sur la deuxième des deux lignes, vous ne vérifiez pas que vous n'êtes pas allé à la fin du conteneur. En effet, si vous entrez dans la boucle interne, vous incrémentez deux fois, mais seulement en vérifiant si vous pouvez incrémenter une fois.

Une solution consiste à vérifier si vous êtes à end() avant de faire le second pas, mais il me semble que vous essayez de préforme la même opération que j'étais my question a while ago à faire avec des éléments de filtrage d'un conteneur (une carte dans ce cas, mais il en va de même pour la plupart des conteneurs STL).

0

Je crois que Chris a raison. Cependant, un autre problème peut provenir du fait que vous affectez à l'itérateur. - Les itérateurs de liste sont-ils garantis? Sans regarder la norme, je ne pense pas car l'assignabilité n'est nulle part mentionnée dans la documentation SGI des itérateurs.

+0

Il semble de http://www.sgi.com/tech/stl/Iterators.html que les itérateurs vers l'avant sont assignables. Les itérateurs de std :: list sont des itérateurs bidirectionnels (http://www.sgi.com/tech/stl/List.html, http://www.sgi.com/tech/stl/ReversibleContainer.html), et sont donc aussi forward itérateurs. :-) –

+0

Hmm, c'est ce qu'ils entendent par "multi-pass"? Car sinon rien n'est dit sur l'assignabilité * de l'itérateur * (par opposition à sa valeur!). –

14

Je Récrire votre boucle pour être comme les suivantes:

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

Maintenant vous Looping correctement dans la liste de vérification tous les index.

+0

Vous n'augmentez pas l'itérateur dans la première partie de l'if –

+1

Je suis - iter = effacer (iter). La fonction d'effacement renvoie le nouvel itérateur après celui qui vient d'être supprimé. –

+0

Oh, c'est vrai, ne me dérange pas. Cela ne fonctionne pas avec certains types de conteneurs, notez –

0

Ceci n'est qu'une note, mais importante. Je suppose que vous héritez d'un std::ist<PCB>. Je dois dire: hériter pour réutiliser la fonctionnalité n'a pas souvent bien tourné pour moi. Mais puisque vous "héritez" du projet, il n'y a rien à faire ...

+0

L'héritage d'implémentation, bien qu'il ne soit pas idéal, peut être pardonné s'il s'agit uniquement d'un héritage privé. :-) –

0

Si vous obtenez un "list itération incompatible" c'est probablement parce que dans votre "ReadyQueue :: getInstance() -> add (* iter); " vous modifiez quelque chose dans * iter qui fait que l'algorithme de hachage retourne une valeur d'effacement différente de celle de l'insertion.

0

Puis-je suggérer un algorithme plus simple?

La fonction libre std::remove_if peut être utilisée pour partitionner votre liste en 2, éléments qui correspondent ou ne correspondent pas au prédicat (c'est-à-dire arrivalTime == 0). Il renvoie l'itérateur séparant les plages. Vous pouvez alors appeler ReadyQueue::getInstance()->add(subrange_begin, subrange_end)(vous avez cette surcharge, non?) et effacer la sous-gamme par la suite. Juste un cas où vous pouvez utiliser des algorithmes STL au lieu d'écrire vos propres boucles.

1

La cause première est "list.erase()" changera l'itérateur. L'écriture correcte pour la boucle « pour »:

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? 
    } 
    } 

Mais il a encore des effets secondaires, donc la solution tandis que Mark sera le meilleur.