VS、UNIX / LinuxでSTLイテレータを消去するとどうなりますか?
-
10-07-2019 - |
質問
次のシナリオを考慮してください:
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;
}
}
コレクションを変更すると、イテレータが無効になると思います。あなたが見つけたように、あなたは行動に頼ることはできません。