イテレータ(iterator)は C++ STL(Standard Template Library)の中核を成す概念のひとつで、ポインタの機能を抽象化したものである。
参照外し(dereference)、インクリメント・デクリメント、比較などのポインタ同等の機能を持つ。
STLの各コンテナには、それ専用のイテレータクラスが定義されており、 イテレータオブジェクトの生成はコンテナの begin(), end() メソッドなどで行う。
コンテナの最大要素を取得する例:
vector<int> cntn; ..... // cntn にデータを格納する処理 int val = MIN_INT; for(vector<int>::iterator itr = cntn.begin(); itr != cntn.end(); ++itr) val = max(val, *itr);
イテレータは、下図の様に、各種アルゴリズムと各種コンテナの仲介を行う。
各アルゴリズムはイテレータを介してコンテナのデータにアクセスするので、コンテナの構造等を知る必要がない。
先の例をテンプレート関数化したもの:
template<typename Iterator, typename T> T get_max_value(Iterator first, Iterator last, T val) { while( first != last ) { val = max(val, *first); ++first; } return val; }
前節のテンプレート関数定義を見ると分かるように、各種アルゴリズムは直接的にはイテレータの機能のみを利用し、
コンテナのデータ構造については感知しない。
したがって、イテレータとして要求されるインタフェースを備えていれば、イテレータの実装は自由である。
イテレータオブジェクトがデータ群またはそれ相当のものを内臓していたり、
イテレータオブジェクトが他のイテレータオブジェクトを参照していてもよい。
利点:
実装例1: (データ内臓型)乱数生成器
template< typename T> class CTestIterator : public std::iterator<std::forward_iterator_tag, T> { int m_counter; // イテレータを区別するためのカウンタ T m_value; // 生成した乱数の値 [0, 100) public: CTestIterator(int counter = 0) : m_counter(counter), m_value(0) { gen_rand(); }; public: T operator*() const { return m_value; } CTestIterator &operator++() { gen_rand(); ++m_counter; return *this; } bool operator!=(const CTestIterator &x) const { return m_counter != x.m_counter; } public: int get_counter() const { return m_counter; }; protected: void gen_rand() { m_value = static_cast<T>(rand() % 100); } };
上記イテレータの使用例:
CTestIterator<int> first(0); CTestIterator<int> last(1000); // 1000回試行 CTestIterator<int> itr = std::find(first, last, 7); // 7が出るまで繰り返す if( itr != last ) cout << "val = " << static_cast<int>(*itr) << " counter = " << itr.get_counter() << endl; else cout << "not found.\n";
実装例2:行単位イテレータ
文字単位イテレータから(CRLF/CR/LF区切りの)行単位イテレータを生成
class const_line_iterator : public std::iterator<std::forward_iterator_tag, std::string> { typedef vector<char>::const_iterator char_iterator; char_iterator m_begin; // 行先頭へのイテレータ char_iterator m_next; // 行末の次の文字 char_iterator m_end; // 行全体の末尾の次 public: const_line_iterator(char_iterator itr, char_iterator end) : m_begin(itr), m_end(end) { set_next(); } public: /*const*/ string operator*() const { return string(m_begin, m_next); } const_line_iterator &operator++() { if( m_begin != m_end ) { m_begin = m_next; set_next(); } return *this; } bool operator==(const const_line_iterator &x) const { return m_begin == x.m_begin; } bool operator!=(const const_line_iterator &x) const { return m_begin != x.m_begin; } protected: void set_next() { char ch = '\0'; // ひとつ前の文字 for(m_next = m_begin; ; ) { if( m_next == m_end || ch == '\n' ) return; if( ch == '\r' ) { if( *m_next == '\n' ) ++m_next; return; } ch = *m_next; ++m_next; } } };
上記イテレータの使用例:
// 各行の内容を行番号付きで表示 void printLine(const vector<char> &v) { int lineNum = 0; const_line_iterator itr(v.begin(), v.end()); const_line_iterator iend(v.end(), v.end()); for( ;itr != iend; ++itr ) cout << ++lineNum << ": " << (*itr).c_str() << "\n"; } // 最大行長を返す int getMaxSize(const vector<char> &v) { size_t maxSize = 0; const_line_iterator itr(v.begin(), v.end()); const_line_iterator iend(v.end(), v.end()); for( ;itr != iend; ++itr ) maxSize = max(maxSize, (*itr).size()); return maxSize; } // std::find() を用いて指定文字列の行を検索 void main() { vector<int> v; ..... // v にデータを格納する処理 const_line_iterator first(v.begin(), v.end()); const_line_iterator last(v.end(), v.end()); const_line_iterator itr = std::find(first, last, string("12345\r\n")); }
その他の実装方法:
boost::iterator_facade を利用する
自前のクラスにイテレータを実装することで、インタフェースを標準化し、
柔軟性・再利用性を高めよう!
アダプタ・イテレータを実装することで、仮想的なデータ構造を構築することも可能だ。