C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[6]: Listによる管理とイテレータでの処理


(過去ログ 81 を表示中)

[トピック内 7 記事 (1 - 7 表示)]  << 0 >>

■47718 / inTopicNo.1)  Listによる管理とイテレータでの処理
  
□投稿者/ 学習中 (1回)-(2010/03/12(Fri) 04:38:42)

分類:[C/C++] 

はじめまして。
現在List管理とイテレータの処理を学習しているのですがいまいちわからないので質問させていただきました。

質問
intListに0〜9までの数字を追加してイテレータを用いてintList内を全てチェックし5だったらeraseで削除しようとしたのですが
うまくいかないのでよろしければ正しい書き方を御教授願えないでしょうか。
初歩的なことかもしれませんがしっかり理解していきたいのでよろしくお願いします。
開発環境はVC++2008EEです。

---------------------------------------------------------------------
#include <list>

void main() {
	std::list<int> intList;

	for (int i = 0; i < 10; i++) {
		intList.push_back(i);
	}

	std::list<int>::iterator it;
	for (it = intList.begin(); it != intList.end(); it++) {
		if (it == 5) {
			intList.erase(it);
		}
	}
}
-----------------------------------------------------------------------

引用返信 編集キー/
■47719 / inTopicNo.2)  Re[1]: Listによる管理とイテレータでの処理
□投稿者/ 774RR (486回)-(2010/03/12(Fri) 07:13:43)
iterator とは要するにポインタなので it==5 ではなく *it==5 と書くわけだ。

引用返信 編集キー/
■47721 / inTopicNo.3)  Re[2]: Listによる管理とイテレータでの処理
□投稿者/ Blue (32回)-(2010/03/12(Fri) 08:44:50)
ループ中に要素を消すのはまずいと思うけど。
std::listなら次の要素をとってからeraseすれば大丈夫なのかな?
~~~~~~~~~~~~~

std::list<int>::iterator it, itErase;
for (it = intList.begin(); it != intList.end(); ) {
itErase = it;
++it;
if (*itErase == 5) {
intList.erase(itErase);
}
}

引用返信 編集キー/
■47722 / inTopicNo.4)  Re[3]: Listによる管理とイテレータでの処理
□投稿者/ 774RR (487回)-(2010/03/12(Fri) 09:31:53)
2010/03/12(Fri) 12:38:04 編集(投稿者)

出勤前にばたばた書き込むとバグるな・・・・もうちょっと解説するか。

iterator は「文法上、ポインタとほぼ同等に振舞うクラス」と考えるといい。

配列があるとき、その要素をポインタでたどる場合、を考える。
int a[1000];
int* p=&a[100]; // があるとしよう。
++p とすると次の要素を指すよう p を書き換えることが出来る。
--p とすると前の要素を指すよう p を書き換えることが出来る。
*p とすると、今指している要素の値を取り出すことが出来る。

配列→コンテナ(コンテナとは vector や list など STL 的に複数値を保持できるものをいう)
ポインタ→イテレータ、と読み替えるといい。
コンテナが中でどのようにデータを保持しているかに関係なく
++it とすると次の要素を指すよう it を書き換えることが出来る
--it とすると前の要素を指すよう it を書き換えることが出来る
*it とすると、今指している要素の値を取り出すことが出来る (参照することが出来る)
イテレータは、このように作ってあるクラスであり、値の比較そのものは *it==5 となる。

次に erase だが、コンテナの要素を erase すると特定条件下にあるイテレータが無効になる。
erase(it) でどんなイテレータが無効になるかはコンテナ次第 (vector と list で挙動が違う)
直観的に理解できると思うが erase したイテレータは必ず無効になるので、
erase(it) の後 ++it しても無意味となりバグってしまう。
これを防ぐには erase は削除した要素の次、を返すので
it = intlist.erase(it); とすれば (vector だろうが list だろうが) OK.

なんだが・・・ it=l.erase(it); だと、ここで1つ先に進んでしまうわけだ。
なので、すぐ直後で ++it してしまうと2つ先に進んでしまうことになりやはりバグってしまう。
++it する場所を移動させておかないとはまるのだ。
とりあえずここまで。
引用返信 編集キー/
■47732 / inTopicNo.5)  Re[4]: Listによる管理とイテレータでの処理
□投稿者/ 学習中 (2回)-(2010/03/12(Fri) 15:58:52)
詳しい説明ありがとうございます。
なるほど、、ポインタ扱いなんですか。勉強になります。

>なんだが・・・ it=l.erase(it); だと、ここで1つ先に進んでしまうわけだ。
>なので、すぐ直後で ++it してしまうと2つ先に進んでしまうことになりやはりバグってしまう。
>++it する場所を移動させておかないとはまるのだ。

なるほど、it = intList.erase(it);をしたあと++itすると1つ要素をとばしてしまうんですね。
以下のプログラムなら問題なく動きそうですね。
--------------------------------------------------------
std::list<int>::iterator it;
for (it = intList.begin(); it != intList.end();) {
	if (*it == 5) {
		it = intList.erase(it);
		continue;
	}
	++it;
}
--------------------------------------------------------

あと少し気になったのですが it++ と ++it では何か変わるのでしょうか?
質問に次ぐ質問で申し訳ないですがよろしければ御教授願います。

引用返信 編集キー/
■47735 / inTopicNo.6)  Re[5]: Listによる管理とイテレータでの処理
□投稿者/ 774RR (488回)-(2010/03/12(Fri) 17:04:15)
おお、これはまさに満点回答、すばらしい。
ということで前回のレスでは故意に挙げなかった参考ページとか。
http://marupeke296.com/TIPS_No12_ListElementErase.html

前置 inc/dec と後置 inc/dec は
・副作用は同じ
・部分式自体の値は違う
というあたりは理解できている、と思う。
int x=3;
int y= x++; // y=3, x=4 になる
int z= ++x; // z=5, x=5 になる

iterator に対して ++ -- を適用するときも同様のセマンティクス(意味)になるように
operator ++ や operator -- は作られているため、
・前置 ++ はイテレータ変数を1つ進め、式の値は進めた後の値になる
・後置 ++ はイテレータ変数を1つ進め、式の値は進める前の値になる
となっている。

純粋にイテレータを前後させたいだけならば式の値は不要なわけで、
・前の値を保持しておく必要が無く実装が簡単になる前置 ++ -- を使うほうがよく、
・前の値を保持しておく必要があり実装が複雑になる後置 ++ -- を使わないほうがよい、
とされている。

引用返信 編集キー/
■47737 / inTopicNo.7)  Re[6]: Listによる管理とイテレータでの処理
□投稿者/ 学習中 (3回)-(2010/03/12(Fri) 17:25:47)
返信ありがとうございます。
参考ページの例が同じでちょっとびっくりしました(笑)
前置式、後置式の説明も納得できました。

今回の質問はこれで解決とさせていただきます。
全ての質問に丁寧に答えてくださって本当にありがとうございました!
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -