質問

次のシナリオを考慮してください:


map(T,S*) & GetMap(); //Forward decleration

map(T, S*) T2pS = GetMap();

for(map(T, S*)::iterator it = T2pS.begin(); it != T2pS.end(); ++it)
{
    if(it->second != NULL)
    {
        delete it->second;
        it->second = NULL;
    }
    T2pS.erase(it);
    //In VS2005, after the erase, we will crash on the ++it of the for loop.
    //In UNIX, Linux, this doesn't crash.
}//for

VS2005では、「erase」の後、イテレータはend()に等しくなるため、インクリメントしようとするとクラッシュします。 ここに示されている動作にコンパイラ間に本当に違いはありますか? その場合、「消去」の後のイテレータは何になりますか? UNIX / Linuxと同等ですか?

ありがとう...

役に立ちましたか?

解決

はい、イテレータを消去すると、そのイテレータはいわゆる特異値を取得します。これは、コンテナに属しなくなったことを意味します。インクリメント、デクリメント、または読み取り/書き込みはできなくなりました。そのループを行う正しい方法は次のとおりです。

for(map<T, S*>::iterator it = T2pS.begin(); it != T2pS.end(); T2pS.erase(it++)) {
    // wilhelmtell in the comments is right: no need to check for NULL. 
    // delete of a NULL pointer is a no-op.
    if(it->second != NULL) {
        delete it->second;
        it->second = NULL;
    }
}

あるイテレータを消去したときに他のイテレータを無効にする可能性のあるコンテナの場合、 erase は次の有効なイテレータを返します。次に、

it = T2pS.erase(it)

それは std :: vector および std :: deque での動作ですが、 std :: map では動作しませんstd :: set

他のヒント

イテレータで erase を呼び出して std :: map にすると、無効になります。これは、使用できないことを意味します。それを使用しようとすると(たとえば、インクリメントすることによって)無効になり、何も起こらないことがあります(クラッシュを含む)。 std :: map の場合、イテレータで erase を呼び出しても他のイテレータは無効になりません。たとえば、この呼び出しの後に( it T2pS.end())ではなく、有効です:

T2pS.erase( it++ );

もちろん、このアプローチを使用する場合、forループで it を無条件にインクリメントすることは望ましくありません。

ただし、この例では、なぜforループで消去するのが面倒ですか?ループの最後でT2pS.clear()を呼び出すだけではどうですか。

一方、マップの「右側」に生のポインタがあるように見えますが、マップはオブジェクトを指すオブジェクトを所有しているように見えます。この場合、マップの右側にあるものをstd :: tr1 :: shared_ptrなどのスマートポインターのようなものにしないのはなぜですか?

[ちなみに、 map へのテンプレートパラメータは表示されません。ローカル名前空間で std :: map の特定のインスタンス化を map としてtypedefしましたか?]

こちら

for (i = v.begin(); i != v.end(); ) {
  //...
  if (erase_required) {
      i = v.erase(i);
  } else {
      ++i;
  }
}

コレクションを変更すると、イテレータが無効になると思います。あなたが見つけたように、あなたは行動に頼ることはできません。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top