6

Visual Studio 2003 を使用してビルドされた古いプロジェクトがあり、最近 vs2005 で再コンパイルしました。ただし、実行時に次のエラーが発生します。

リスト反復子はインクリメントできません

プログラムをこの関数までたどりました:

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

私は C++ の専門家ではありませんが、これは VS デバッガーが得た限りです。誰かが私に何が問題なのか説明してもらえますか?

ありがとう

4

8 に答える 8

14

ループを次のように書き直します。

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

これで、すべてのインデックスをチェックするリストを正しくループしています。

于 2008-10-13T08:20:07.363 に答える
9

if の場合、リスト イテレータは 2 回インクリメントされることに注意してくださいiter->arrivalTime == 0。1 回は要素の削除前、もう 1 回はループの最後です。

削除する項目がリストの最後の項目である場合、これは明らかに正しく機能しません。VS2003 でも正しく動作しなかったとあえて言いますが、VS2005 はそれについてより適切に警告します。:-)

過去を反復するのは未定義の動作であることを忘れないでくださいend()。プログラムのクラッシュや (この場合は) エラー メッセージなど、あらゆることが発生する可能性があります。

于 2008-10-13T08:17:20.053 に答える
1

根本的な原因は、「list.erase()」がイテレータを変更することです。「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?
    }
   }

ただし、それでも副作用があるため、Mark の while ソリューションが最適です。

于 2013-11-06T16:31:36.757 に答える
1

問題がどこにあるかを示すために、コードの数行を省略します。

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

                iter++; // ***

        }
    }

*** とマークされた 2 行では、反復子をインクリメントしています。問題は、2 行のうちの 2 行目で、コンテナーの最後まで行っていないことを確認していないことです。事実上、内側のループに入ると、2 回インクリメントされますが、1 回インクリメントできるかどうかのみがチェックされます。

1 つの解決策は、2 番目のインクリメントを行う前に、現在の状態にあるかどうかを確認することですが、コンテナからアイテムをフィルタリングするために、先ほど質問したend()のと同じ操作を実行しようとしているように見えます(その場合、ほとんどの STL コンテナーに同じことが当てはまります)。

于 2008-10-13T08:19:32.217 に答える
0

これは余談ですが、重要なことです。

から継承していると思いますstd::ist<PCB>。私が言わなければならないのは、機能を再利用するために継承することは、私にとってあまりうまくいかないことが多いということです。しかし、あなたもプロジェクトを「継承」しているので、それについて何もする必要はありません...

于 2008-10-13T08:26:49.817 に答える
0

「list iterator incompatible」が発生した場合、おそらく「ReadyQueue::getInstance()->add(*iter);」内にあるためです。*iter で何かを変更しているため、ハッシュ アルゴリズムは、挿入時とは異なる値を消去に返します。

于 2008-10-13T09:32:13.087 に答える
0

私はクリスが正しいと信じています。ただし、反復子に割り当てるという事実から、別の問題が発生する場合があります。– リスト反復子は代入可能であることが保証されていますか? 標準を見なければ、イテレータの SGI ドキュメントで代入可能性がどこにも言及されていないため、そうは思いません。

于 2008-10-13T08:19:57.910 に答える
0

より単純なアルゴリズムを提案できますか?

free 関数std::remove_ifを使用して、述語に一致する要素と一致しない要素 (つまり、arrivalTime==0) の 2 つの要素にリストを分割できます。範囲を区切る反復子を返します。その後、呼び出しReadyQueue::getInstance()->add(subrange_begin, subrange_end) て (そのオーバーロードがありますよね?)、後でサブレンジを消去できます。

独自のループを作成する代わりに、STL アルゴリズムを使用できる場合です。

于 2008-10-13T09:56:57.607 に答える