frostar@wiki
Java風Iterator
最終更新:
frostar
-
view
C++ (fork) Advent Calendar 2013の12/7分の記事として
C++のイテレータについてちょっと書いていこうかなと。
本当はブログに書くものらしいんですが、プログラム系のブログはやってないのでwikiで失礼します。
私自身としてはC++に関して初級者と中級者の間くらいなのであんまり期待しないで読んでいただけるといいかなと。
C++のイテレータについてちょっと書いていこうかなと。
本当はブログに書くものらしいんですが、プログラム系のブログはやってないのでwikiで失礼します。
私自身としてはC++に関して初級者と中級者の間くらいなのであんまり期待しないで読んでいただけるといいかなと。
さて、本題です。
C++のSTLにはlistやvectorなどのコンテナ(データを格納する構造)があります。
コンテナ内の要素を一つずつ取り出すには(vectorとかだとインデックスアクセスで行うときもありますが)通常はイテレータを使用します。
C++のSTLにはlistやvectorなどのコンテナ(データを格納する構造)があります。
コンテナ内の要素を一つずつ取り出すには(vectorとかだとインデックスアクセスで行うときもありますが)通常はイテレータを使用します。
vector<int>::iterator it = data.begin(); while(it!=data.end()){ std::cout << *it++ << endl; }
しかし、C++のイテレータは上の例のようにbeginで最初の要素を指すイテレータを取得し、
それをendになるまでインクリメントしてアクセスするというポインタに近いようなやり方で行っています。
それをendになるまでインクリメントしてアクセスするというポインタに近いようなやり方で行っています。
個人的にはイテレータは要素の取り出しを抽象化するものだと考えているので、
このようなやり方は少し野暮ったく感じます。
例えばJavaだとイテレータは以下のように使用します。
このようなやり方は少し野暮ったく感じます。
例えばJavaだとイテレータは以下のように使用します。
Iterator<Integer> it = data.iterator(); while(it.hasNext()){ System.out.println(it.next()); }
このようにIntegerのIteratorというような形で抽象化されているため、
SetでもListでも、もっと言えばコンテナじゃなくても
要素を取り出すという機能を実装してれば順次取り出しが可能です。
また、それぞれの処理をメソッド内に閉じ込めてるので
その中にいろいろ処理を書けば特殊な取り出し方をするイテレータも実装可能です。
SetでもListでも、もっと言えばコンテナじゃなくても
要素を取り出すという機能を実装してれば順次取り出しが可能です。
また、それぞれの処理をメソッド内に閉じ込めてるので
その中にいろいろ処理を書けば特殊な取り出し方をするイテレータも実装可能です。
そこで、C++のイテレータをJava風にラップして使ってみます。
JavaのIteratorインターフェースで定義されているのは以下の3つのメソッドです。
JavaのIteratorインターフェースで定義されているのは以下の3つのメソッドです。
boolean hasNext() //繰り返し処理でさらに要素がある場合に true を返します
T next() //繰り返し処理で次の要素を返します
void remove() //最後に返された要素を元のコンテナから削除します
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自体もラップしてしまって、
ラップしたクラスからイテレータを返すようにするといいかもしれないです。
実装に関してはいろいろとやり方がありますが、
インターフェースに沿ってさえいれば抽象的に扱えるよという話でした(イテレータに限った話ではないですが)。
removeを実装しないならbeginとendをフィールドにしたほうが
もうちょっと全体としてすっきりまとまるかもしれません。
もっとオブジェクト指向っぽく書くならvector自体もラップしてしまって、
ラップしたクラスからイテレータを返すようにするといいかもしれないです。
実装に関してはいろいろとやり方がありますが、
インターフェースに沿ってさえいれば抽象的に扱えるよという話でした(イテレータに限った話ではないですが)。