(回転)C++STLにおけるmap.erase(it++)用法原理解析
3628 ワード
元のアドレス:https://blog.csdn.net/liuzhi67/article/details/50950843
以前、コードでmap::erase関数を使用していたとき、vector::eraseの使い方を誤って転載し、Server downが落ちてしまいましたが、テスト環境で問題を発見し、オンラインになる前に救済==を行いました. 以下map::eraseの正しい使い方をまとめます. まず、サイクルでvector::eraseを使うときに慣れている使い方を見てみましょう.という使い方は問題ありません.
しかし、当然map::eraseで上のeraseの使い方をそのまま使うと思うと、問題があります.http://www.cplusplus.com/reference/map/map/erase/ の説明を参照してください.
ループでmap::eraseを正しく使う方法は何ですか.次のようになります.
2、パラメータ処理は関数呼び出しよりも優先されるため、it++操作、すなわちitが次のアドレスを指している.
3、erase関数を呼び出し、最初のステップで保存した削除するitの値の一時変数が指す位置を解放します. しかし、個人的には理解しにくいと思います.最初のステップはitの値を関数呼び出しのパラメータに渡してから、i+1の操作を実行することを意味しますか?このようにit++の実行は強引に二つに切られているような気がして、この結論をしっかり覚えるしかありません. 後に『STLソース剖析』の++iとi++の実現方式の違いを見て、そしてある日、『More Effective C++』の説明を見て、突然理解して、mapInt.erase(it++)のメカニズムはついに神秘的ではありません. 実はmapInt.erase(it++)では、it++は確かに完全な実行プロセスとして、it++の具体的な実装コードは以下のようになっています.
特筆すべきは、最新のC++11規格ではmap::erase関数が実行されると次の要素に戻るiteratorが追加されていますが、C++11が現在のC++98のカバーレベルに達するのはいつなのか分かりません.慎重にmap.erase(it++)を使用して保険を比較することです. http://www.cplusplus.com/reference/map/map/erase/最後に、接頭辞++と接尾辞++の戻り値の1つは反復器参照であり、1つは反復器伝値である理由を尋ねる仲間もいるかもしれません.簡単に言えば、接頭辞++は伝達された反復器を返し、自然に反復器自体の参照を返すことができるが、接尾辞++は関数内部の一時変数を返し、関数が実行された後に分析され、必ず伝達できない.注意値を伝達する方式で返す以上、その戻り値の修正は元のitには影響しないが、例えば(it+)++の結果は実際にはitが1回だけ増加し、2回目の++はその(it+)の戻り値に対して++を実行しただけで、元のitには何の効果もない.
以前、コードでmap::erase関数を使用していたとき、vector::eraseの使い方を誤って転載し、Server downが落ちてしまいましたが、テスト環境で問題を発見し、オンラインになる前に救済==を行いました. 以下map::eraseの正しい使い方をまとめます. まず、サイクルでvector::eraseを使うときに慣れている使い方を見てみましょう.
for(vector::iterator it = vecInt.begin(); it != vecInt.end();)
{
if(*it == 0)
{
it = vecInt.erase(it);
}
else
{
it++;
}
}
プログラムは1つのvectorから値0の要素を削除し、vector::erase関数を使用してiteratorに基づいてある要素を削除すると、次の要素のiteratorの性質が返されます. http://www.cplusplus.com/reference/vector/vector/erase/C++98
iterator erase (iterator position);
しかし、当然map::eraseで上のeraseの使い方をそのまま使うと思うと、問題があります.http://www.cplusplus.com/reference/map/map/erase/ の説明を参照してください.
C++98
(1)
void erase (iterator position);
(2)
size_type erase (const key_type& k);
(3)
void erase (iterator first, iterator last);
以上のように,C++98におけるmap::eraseはiteratorの値を持つプロトタイプ関数を返さない. では、問題はit=map.erase(it)で、itを操作すると何が起こるのでしょうか.伝説の「未定義の行為」が起こります!プログラムの停止、機械の死機、地球地震、宇宙破壊などが含まれているが、それに限らない.原因は何なのか.map.erase(it)を実行した後、itというiteratorは失効しました.C言語の失効したポインタを考慮して、再び引用するとどんな問題が発生しますか?ループでmap::eraseを正しく使う方法は何ですか.次のようになります.
for(map::iterator it = mapInt.begin(); it != mapInt.end();)
{
if(it->second == 0)
{
mapInt.erase(it++);
}
else
{
it++;
}
}
ネット上でmapInt.erase(it++)の説明を探して、比較的に詳しい1種の解釈は: http://blog.csdn.net/lmh12506/article/details/9167653 この方法では接尾辞++の特徴を利用し,このときmapInt.erase(it++)を実行する.この文は三つの過程に分かれている. 1、itの値を一時変数に割り当ててeraseに渡すパラメータ変数とする2、パラメータ処理は関数呼び出しよりも優先されるため、it++操作、すなわちitが次のアドレスを指している.
3、erase関数を呼び出し、最初のステップで保存した削除するitの値の一時変数が指す位置を解放します. しかし、個人的には理解しにくいと思います.最初のステップはitの値を関数呼び出しのパラメータに渡してから、i+1の操作を実行することを意味しますか?このようにit++の実行は強引に二つに切られているような気がして、この結論をしっかり覚えるしかありません. 後に『STLソース剖析』の++iとi++の実現方式の違いを見て、そしてある日、『More Effective C++』の説明を見て、突然理解して、mapInt.erase(it++)のメカニズムはついに神秘的ではありません. 実はmapInt.erase(it++)では、it++は確かに完全な実行プロセスとして、it++の具体的な実装コードは以下のようになっています.
// postfix form: fetch and increment
map::iterator operator++(int)// int prefix++
{
map::iterator tmp = *this; // fetch
increment(); // increment,map , iterator
return tmp; // return what was
}
上のコードの最終的な戻り値は実はtmpで、tmpは*thisの古い値を格納して、thisは後にincrement関数によって自増しましたが、tmpのは依然として元の値を維持して、最後にtmpを返してeraseのパラメータとして付与して、だからmapInt.erase(it++)の中で、実はit++は1つの全体として実行して完成して、erase関数に値を伝える前に、it自身は実はすでに+1して、しかし、接尾辞++は+1操作を実行していない古い値を返すので、後のerase関数は元のit位置の値を削除し、反復器は失効しますが、前のitは+1自増しているので、影響を受けませんよ. 上記のコードで呼び出されたプレフィックス++コードについては、次のようになります.// prefix form: increment and fetch
map::iterator& operator++()
{
increment(); // increment
return *this; // fetch
}
接尾辞++は、接頭辞++の操作よりも一時変数が1つ多く、呼び出し元に値コピーで返されるため、一般的に接尾辞++の効率は接頭辞++よりも低い.特筆すべきは、最新のC++11規格ではmap::erase関数が実行されると次の要素に戻るiteratorが追加されていますが、C++11が現在のC++98のカバーレベルに達するのはいつなのか分かりません.慎重にmap.erase(it++)を使用して保険を比較することです. http://www.cplusplus.com/reference/map/map/erase/
C++11
(1)
iterator erase (const_iterator position);
(2)
size_type erase (const key_type& k);
(3)
iterator erase (const_iterator first, const_iterator last);