2008-10-13 9 views
6

Ich habe ein altes Projekt, das mit Visual Studio 2003 erstellt wurde und ich es vor kurzem mit VS2005 neu kompiliert. Doch während der Laufzeit, erhalte ich folgende Fehlermeldung:Liste Iterator nicht inkrementell

Liste Iterator nicht inkrementierbarer

ich das Programm auf diese Funktion zurückverfolgt:

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

Ich bin kein C++ Experten und das ist so weit wie der VS Debugger mich hat. Könnte mir jemand erklären, was das Problem ist? einmal vor dem Element Entfernung und noch einmal am Ende der Schleife: Wenn iter->arrivalTime == 0, dann wird die Liste Iterator zweimal erhöht

Dank

Antwort

9

Beachten Sie, dass.

Wenn das Element, das entfernt werden soll, das letzte Element in der Liste ist, wird dies offensichtlich nicht korrekt funktionieren. Ich wage zu sagen, dass es sogar in VS2003 nie richtig funktioniert hat, aber VS2005 warnt Sie besser. :-)

Denken Sie daran, es ist undefiniertes Verhalten zu Iterieren nach end(). Es kann absolut alles passieren, wie zum Beispiel Programmabsturz oder (in diesem Fall) eine Fehlermeldung.

1

Ich werde nur ein paar Zeilen Code elide zu zeigen, wo das Problem liegt: den Iterator

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

       iter++; // *** 

     } 
    } 

An den beiden Linien markiert *** Sie erhöht wird. Das Problem ist, dass Sie in der zweiten der beiden Zeilen nicht überprüfen, ob Sie nicht bis zum Ende des Containers gegangen sind. Effektiv, wenn Sie in die innere Schleife kommen, werden Sie zweimal erhöht, aber nur überprüft, ob Sie in der Lage sind, einmal zu inkrementieren. Eine Lösung ist es, zu überprüfen, ob Sie bei end() sind, bevor Sie das zweite Inkrement machen, aber es sieht für mich aus, als ob Sie versuchen, die gleiche Operation wie in my question a while ago zu machen mit Filtern von Elementen aus einem Container (eine Karte) in diesem Fall gilt das gleiche für die meisten STL-Container).

0

Ich glaube, Chris hat Recht. Ein anderes Problem kann jedoch von der Tatsache herrühren, dass Sie dem Iterator zuweisen. - Sind Listen-Iteratoren garantiert zuweisbar? Ohne auf den Standard zu schauen, glaube ich nicht, weil die Zuordnungsfähigkeit in der SGI-Dokumentation der Iteratoren nirgends erwähnt wird.

+0

Es scheint von http://www.sgi.com/tech/stl/Iterators.html, dass vorwärts Iteratoren zuweisbar sind. Die Iteratoren von std :: list sind bidirektionale Iteratoren (http://www.sgi.com/tech/stl/List.html, http://www.sgi.com/tech/stl/ReversibleContainer.html) und sind somit auch Iteratoren weiterleiten. :-) –

+0

Hmm, ist das was sie mit "multi-pass" meinen? Weil sonst nichts über die Zuweisbarkeit * des Iterators * gesagt wird (im Gegensatz zu seinem Wert!). –

14

Ich würde wieder schreiben Sie Ihre Schleife wie folgt zu sein:

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

Jetzt sind Sie richtig durch die Liste Looping jeden Index zu überprüfen.

+0

Sie erhöhen den Iterator nicht im ersten Teil des if –

+1

I am - iter = erase (iter). Die Löschfunktion gibt den neuen Iterator nach dem gerade gelöschten zurück. –

+0

Oh, macht mir nichts aus. Dies funktioniert nicht mit bestimmten Arten von Containern, wohlgemerkt –

0

Dies ist nur eine Nebenbemerkung, aber eine wichtige.

Ich vermute, Sie erben von einem std::ist<PCB>. Ich muss sagen: Die Wiederverwendung von Funktionen ist für mich oft nicht gelungen. Aber da Sie das Projekt auch 'erben', gibt es nicht viel zu tun ...

+0

Implementierung Vererbung, obwohl nicht ideal, kann verzeihlich sein, wenn es nur private Vererbung ist. :-) –

0

Wenn Sie "list iterator incompatible" erhalten, liegt es wahrscheinlich daran, dass Sie innerhalb Ihrer "ReadyQueue :: getInstance() -> hinzufügen (* iter); " Sie ändern etwas in * iter, das bewirkt, dass der Hashalgorithmus beim Löschen einen anderen Wert zurückgibt als während des Einfügens.

0

Darf ich einen einfacheren Algorithmus vorschlagen?

Die freie Funktion std::remove_if kann verwendet werden, um Ihre Liste in 2 zu partitionieren, die mit dem Prädikat übereinstimmen oder nicht übereinstimmen (d. H. ArrivalTime == 0). Es gibt den Iterator zurück, der die Bereiche trennt. Sie können dann ReadyQueue::getInstance()->add(subrange_begin, subrange_end)anrufen (Sie haben diese Überlastung, richtig?) und löschen Sie den Teilbereich danach.

Nur ein Fall, in dem Sie STL-Algorithmen verwenden können, anstatt Ihre eigenen Schleifen zu schreiben.

1

Die Ursache ist "list.erase()" wird den Iterator ändern. Die richtige Schreib für „für“ Schleife:

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

Aber es hat noch Nebenwirkung, so Marks während Lösung wird das Beste sein.