frostar@wiki

メニュー

作ったもの

過去の遺物
TRPG
プログラム
リンク

自分関係

ここのページへのリンクについてはこちら
TRPGのセッション記録とかメインのブログ
生配信チャンネル。プログラム<雑談
気が向いたときにやります
個人的な質問に答えてます。

TRPG系

お世話になっているオンラインセッションサイト
TRPGのことならここ!
IRCサーバなど、お世話になってます。
ネクロニカの支援サイトです。ツール開発やオンセもやっています。

プログラム系

初心者のためのプログラムのページ。WindowsSDKの方についても詳しく書いています。
windowsSDKのテクニックがいろいろ書いてあります。
C++STL(標準テンプレートライブラリ)の日本語版リファレンスです。


更新履歴

取得中です。


Java風Iterator


※上記の広告は60日以上更新のないWIKIに表示されています。更新することで広告が下部へ移動します。

C++ (fork) Advent Calendar 2013の12/7分の記事として
C++のイテレータについてちょっと書いていこうかなと。
本当はブログに書くものらしいんですが、プログラム系のブログはやってないのでwikiで失礼します。
私自身としてはC++に関して初級者と中級者の間くらいなのであんまり期待しないで読んでいただけるといいかなと。

さて、本題です。
C++のSTLにはlistやvectorなどのコンテナ(データを格納する構造)があります。
コンテナ内の要素を一つずつ取り出すには(vectorとかだとインデックスアクセスで行うときもありますが)通常はイテレータを使用します。

vector<int>::iterator it = data.begin();
while(it!=data.end()){
	std::cout << *it++ << endl;
}

しかし、C++のイテレータは上の例のようにbeginで最初の要素を指すイテレータを取得し、
それをendになるまでインクリメントしてアクセスするというポインタに近いようなやり方で行っています。

個人的にはイテレータは要素の取り出しを抽象化するものだと考えているので、
このようなやり方は少し野暮ったく感じます。
例えばJavaだとイテレータは以下のように使用します。

Iterator<Integer> it = data.iterator();
while(it.hasNext()){
	System.out.println(it.next());
}

このようにIntegerのIteratorというような形で抽象化されているため、
SetでもListでも、もっと言えばコンテナじゃなくても
要素を取り出すという機能を実装してれば順次取り出しが可能です。
また、それぞれの処理をメソッド内に閉じ込めてるので
その中にいろいろ処理を書けば特殊な取り出し方をするイテレータも実装可能です。

そこで、C++のイテレータをJava風にラップして使ってみます。
JavaのIteratorインターフェースで定義されているのは以下の3つのメソッドです。

boolean hasNext() //繰り返し処理でさらに要素がある場合に true を返します
T next() //繰り返し処理で次の要素を返します
void remove() //最後に返された要素を元のコンテナから削除します

とりあえず上の3つのメソッドを持つIteratorインターフェースを設計します。

template <class T>
interface Iterator{
	virtual BOOL hasNaxt()=0;
	virtual T next()=0;
	virtual void remove()=0;
};

これを使って、例えばvector用のイテレータを作ると以下のようになります。

template <class T>
class VectorIterator:public Iterator<T>{
private:
	vector<T>* data;
	typename std::vector<T>::iterator it;
public:
	VectorIterator(vector<T> &_d){
		data = &_d;
		it = data->begin();
	}
	BOOL hasNaxt(){
		return !(it<data->end());
	}
	T next(){
		T cur = *it;
		it++;
		return cur;
	}
	void remove(){
		it = data->erase(--it);
	}
};


実際に使うときはデータを格納したvectorを引数としてVectorIteratorのインスタンスを生成してから
使用します。

Iterator<int>* it = new VectorIterator<int>(data);
while(!it->hasNaxt()){
	std::cout << it->next() << std::endl;
}

removeすると元のデータから最後にnextで呼んだデータが削除されます。

while(!it->hasNaxt()){
	if(it->next()==5)vit->remove();
}
for(unsigned int i=0;i<data.size();i++){
	std::cout << data[i] << std::endl;
}

これを実行すると元のデータから値が5の要素が削除されてることが確認できます。

今回はremove実装のためにvectorのポインタを保持するという方法をとってますが、
removeを実装しないならbeginとendをフィールドにしたほうが
もうちょっと全体としてすっきりまとまるかもしれません。
もっとオブジェクト指向っぽく書くならvector自体もラップしてしまって、
ラップしたクラスからイテレータを返すようにするといいかもしれないです。
実装に関してはいろいろとやり方がありますが、
インターフェースに沿ってさえいれば抽象的に扱えるよという話でした(イテレータに限った話ではないですが)。
名前:
コメント: