[Tools.h++]10.4 コレクションクラスの反復子
コレクションクラスの多くは、関連付けられた反復子を持ちます。独自の内部状態を保持するという特性により、反復子には以下のような 2 つの利点があります。
-
同じコレクションクラスから複数の反復子を生成できます。
-
一度の走査で、項目すべてにアクセスする必要はありません。
次の例に示すように、反復子は常にコレクションクラス自体から生成されます。
RWBinaryTree bt; . . . RWBinaryTreeIterator bti(bt);
反復子が生成された直後、または reset() が呼び出された後では、反復子の状態は未定義です。反復子の現在の状態または位置を使用する前に、反復子を進ませるか、あるいは位置決めを行なう必要があります。
Tools.h++ の従来の反復子の場合は、コレクションクラスに関連する別個のクラスとして宣言されており、規則は「進めてから戻る」です。[1] ただし、標準 C++ ライブラリを使って実装したクラスから直接取得した反復子は、異なります。コンテナクラスの規準に沿って、反復子は次の決まりに従います。メソッド begin() や end() を使って、あるいは反復子を返すアルゴリズムを使って反復子を取得した場合、それは「標準ライブラリ」の反復子です。[2]
標準ライブラリの反復子は、コンテナ内の項目を参照するかどうかを決めるために、常にそのコレクションの end() 反復子に対して比較しなければなりません。
10.4.1. 伝統的な Tools.h++ の反復子
伝統的な Tools.h++ の反復子は独自の機能を数多く備えています。
反復子の状態は、生成または reset() を呼び出した直後は未定義であることに注意してください。反復子がアクティブ状態のときに、オブジェクトを追加または削除してコレクションクラスを直接変更した場合も[3]、反復子は未定義の状態になります。その時点で反復子を使用すると、予期しない結果を招く可能性があります。そこで、反復子を生成した直後のように、メンバ関数 reset() を使用して反復子を再開始する必要があります。
どの瞬間でも、反復子はコレクションクラスのオブジェクトの 1 つにマークを付けます。これを現在のオブジェクトと考えてください。このマークを移動するには、いろいろな方法があります。例えば、ほとんどの場合、メンバ関数 operator() が使用できます。Tools.h++ では、反復子は常に次のオブジェクトに進み、関連付けられているコレクションクラスが値ベースか参照ベースかにより、TRUE または次のオブジェクトへのポインタを返すように設計されています。コレクションクラスの最後に到達すると、反復子は常に FALSE (すなわちゼロ) を返します。次に、反復子を使用した簡単な標準形式を示します。
RWSlistCollectable list;
.
.
.
RWSlistCollectableIterator iterator(list);
RWCollectable* next;
while (next = iterator()) {
.
. // (次を使用)
.
}
この代わりに、前置インクリメント演算子 ++X を使うこともできます。反復子の中には、findNext() や removeNext() など、マークを操作するメンバ関数を持つものもあります。
メンバ関数 key() も、コレクションクラスが値ベースか参照ベースかにより、それぞれ常に現在のオブジェクト自体か、そのポインタを返します。
ほとんどのコレクションクラスでは、反復子を使うよりもメンバ関数 apply() を使って各メンバにアクセスする方がずっと高速です。これは、特にソート済みのコレクションクラスの場合に言えます。ソート済みのコレクションクラスでは、通常ツリーを走査しなければならず、ノードの親をスタックに格納する必要があります。関数 apply() がプログラムのスタックを使用するのに対し、ソート済みコレクションクラスの反復子は独自にスタックを管理しなければなりません。そのため、apply() の方がずっと高速となります。