コンテナの要素を削除するときは注意してください.

3721 ワード

メモリが解放されると、そのポインタを指すすべてのポインタが「野ポインタ」になります.容器の1つの要素が削除されると、その要素を指すすべての反復器も無効になります.どちらもプログラマーの大敵だ.前者については、十分な警戒心を持っており、メモリを解放した後、すぐにそのポインタをNULLに割り当て、どのポインタを使用する前に空のポインタであるかを判断する(空のポインタであるかどうかを判断する)良い習慣を長年身につけています.後者については、経験豊富なハンターでも時々罠に落ちている.
容器cがあると仮定すると、以下のように定義される.
AssocContainer<int> c;

次に、cに特定の値が含まれている要素、例えば2012を削除します.私たちが最初に考えたのは、ループ、判断、削除です.以下のようにします.
for (AssocContainer<int>::iterator i = c.begin();
	i!= c.end();
	++i) {
	if (2012 == (*i)) c.erase(i);
}

簡単そうに見えますが、残念なことに、このやり方には未定義の行為が含まれており、間違っています.
前述したように、コンテナの要素の1つが削除されると、その要素を指すすべての反復器も無効になります.c.erase(i)が戻ると、iは失効します.すると、forループの++i部分は未定義の動作となる.
「Effective STL」では、eraseを呼び出す前にcの次の要素の反復器が得られることを保証するために、後置的に増加する方法を示しています.次のようになります.
for (AssocContainer<int>::iterator i = c.begin();
	i != c.end();
	/*nothing*/ ){	// for          ;i       
	if (2012 == (*i)) c.erase(i++);	//        ,
					//     i  erase,    i
	else ++i;	//      ,    i
}

「Effective STL」では、すべての容器についてまとめています.次のようになります.
1.コンテナ内の特定の値を持つすべてのオブジェクトを除去します.
1)コンテナがvector、stringまたはdequeの場合、erase-remove慣用法を使用します.
c.erase(remove(c.begin(), c.end(), 2012), c.end()); 

2)コンテナがlistの場合、list::removeを使用します.
c.remove(2012);

3)コンテナが標準関連コンテナの場合、そのeraseメンバー関数を使用します.
c.erase(2012);

2.一つの容器の中で一つの特定の判定式を満たす全ての対象を取り除く:
例えば、この特定判定式は以下のように定義される.
bool badValue(int x) {
	return(x == 2012);
}

1)コンテナがvector、stringまたはdequeの場合、erase-remove_を使用します.if慣用法.
c.erase(remove_if(c.begin(), c.end(), badValue), c.end());

2)容器がlistの場合、list::remove_を使用if.
c.remove_if(badValue);

ご覧のように、シーケンスコンテナ(vector、string、deque、list)では、各removeをremoveに置き換えるだけです.ifでいいです.
3)コンテナが標準的な関連コンテナの場合、コンテナ要素を巡回するためにループを書きます.反復器をeraseに渡すと、後で増やしてください.
//         。

3.ループ内で何かをする(オブジェクトを削除する以外):
1)コンテナが標準シーケンスコンテナの場合、コンテナ要素を巡るループを書き、eraseを呼び出すたびにその戻り値で反復器を更新することを覚えています.
for (SeqContainer<int>::iterator i = c.begin();
	i != c.end();){
	if (2012 == (*i)){
		// go on the Noah's Ark
		i = c.erase(i);		//    erase    
	}				//   i   i  
	else
		++i;
}

2)コンテナが標準的な関連コンテナの場合、コンテナ要素を巡回するためにループを書きます.反復器をeraseに渡すと、後で増やしてください.
for (AssocContainer<int>::iterator i = c.begin();
	i !=c.end();){
	if (2012 == (*i)){
		// go on the Noah's Ark
		c.erase(i++);	//     ,      i
	}
	else ++i;
}