C++言語解説:STL基礎(2)反復子
2003-08-10

[概要] コンテナの要素へアクセスするために、STLでは反復子を利用します。ここでは反
    復子の概要とプログラミング例を学習します。

[構成]・反復子の概要
     * 反復子とは
     * 反復子のタイプ
   ・vectorコンテナと反復子
     * 反復子を使用するメンバ関数
     * 反復子の利用例


●反復子の概要

 ◆反復子とは
 
  ・反復子(iterator)コンテナ内の要素を指す汎用的ポインタです。STLにおけるコ
   ンテナへのアクセスは、この反復子を介して行うのがメインです。反復子はポイン
   タと同様な演算(++,--,+,-)によって要素へアクセスする
ことができます。
  
  ・前回レポートの例では、vectorコンテナに対し[]添字演算子でアクセスしましたが
   当然vectorコンテナも反復子を持っています。

  ・そこで今回は反復子の概要を理解し、反復子を使用したプログラミング例を見るこ
   とにします。
  

 ◆反復子のタイプ
 
  ・反復子には5種類のタイプがあります。
  
   * ランダムアクセス反復子(random access iterator)
     + 値の設定及び取得の両方に適用できます。
     + ランダムアクセスが可能です。
     + 演算子は ++,--,+,- を使用できます。
     + 添字演算子[]も使用できます。
   
   * 双方向反復子(bidirectional iterator)
     + 値の設定及び取得の両方に適用できます。
     + 前方又は後方に移動できます。
     + 演算子は ++,-- が使用できます。
   
   * 前方反復子(forward iterator)
     + 値の設定及び取得の両方に適用できます。
     + 前方にのみ移動できます。
     + 演算子は ++ が使用できます。

   * 入力反復子(input iterator)
     + 値の取得に適用できます。
     + 前方にのみ移動できます。
     + 演算子は ++ が使用できます。

   * 出力反復子(output iterator)
     + 値の取得に適用できます。
     + 前方にのみ移動できます。
     + 演算子は ++ が使用できます。
  
  ・反復子の種類はコンテナにより異なります。それはコンテナの性質から、最も効率
   が良いと思われる反復子が割り当てられていることを意味します。
  

●vectorコンテナと反復子

 ◆反復子を使用するメンバ関数
 
  ・vectorコンテナで反復子を使用するメンバ関数をリストアップします。
  
   [List1.vectorでiteratorを使用するメンバ関数]
  ┌────────────────────────────────────┐
   * iterator begin();
     + vectorの先頭位置を示す反復子を返す
   
   * iterator end();
     + vectorの末尾を示す反復子を返す
   
   * iterator erase(iterator i);
     + iが示す要素を削除し、次の要素を示す反復子を返す
   
   * iterator erase(iterator start, iterator end);
     + startからendまでの要素を削除し、次の要素を示す反復子を返す
   
   * iterator insert(iterator i, const T& val);
     + iが指す要素の直前にvalを挿入
   
   * void insert(iterator i, int num, const T& val);
     + iが指す要素の直前にvalをnum個挿入
   
   * template <class InIter>
      void insert(iterator i, InIter strat, InIter end);

     + iが示す要素の直前に、startからendの要素を挿入
  └────────────────────────────────────┘

  ・iteratorの指し示す位置はFig.1のようになります。特にend()の指す位置には注意
   が必要です。

  ┌────────────────────────────────────┐
    vectorコンテナのVectorオブジェクト
    ┌─┬─┬─┬─┬─┬─  ─┬─┬─┬─┬─┐
    │■│●│▲│◆│●│ ・・・ │◆│■│●│▲│
    └─┴─┴─┴─┴─┴─  ─┴─┴─┴─┴─┘
    ↑ ↑   ↑                
    │ i+1   i+3                Vector.end();
    │ i++                    ※最後の要素ではない
   i=Vector.begin();
  └────────────────────────────────────┘
   Fig.1 iteratorの示す位置


 ◆反復子の利用例
 
  ・反復子を利用してvectorコンテナにアクセスするプログラム例を示します。
  
   [List2.反復子によるvectorコンテナ要素へのアクセス]
  ┌────────────────────────────────────┐
   // +----+
   // | 80 | データファイルを標準入力から読む
   // | 60 |  合計 : 480
   // | 50 |  平均 : 68
   // | 70 |  データ個数 : 7
   // | 60 |  データ要素表示
   // | 90 | 3番目のデータ(50)前に、全てのデータを挿入
   // | 70 |  合計 : 960
   // +----+  平均 : 68
   //      データ個数 : 14
   //      データ要素表示
   
   #include <iostream>
   #include <vector>
   using namespace std;
   
   int  Sum(vector<int>& Vector);      // 合計
   float Average(vector<int>& Vector);    // 平均
   void Display(vector<int>& Vector);    // 要素表示
   
   int main() {
     vector<int> Vector; //空のvector
     vector<int>::iterator ip; //vector<int>のiterator
     int i_data;
     
     while(!cin.eof()){
       cin >> i_data;
       Vector.push_back(i_data);
     }
     
     cout << "合計 : " << Sum(Vector) << endl;
     cout << "平均 : " << Average(Vector) << endl;
     cout << "データ個数 : " << Vector.size() << endl;
     Display(Vector);
     
     vector<int> Vector2(Vector); //VectorをコピーしたVector2生成
     
     ip = Vector.begin(); // ipはVectorの先頭
     ip += 2;       // 3番目の要素へ移動
     
     //3番目の手前(2番目)にVecto2全要素を挿入
     Vector.insert(ip, Vector2.begin(), Vector2.end());
     
     cout << "合計 : " << Sum(Vector) << endl;
     cout << "平均 : " << Average(Vector) << endl;
     cout << "データ個数 : " << Vector.size() << endl;
     Display(Vector);
     
     return 0;
   }
   
   int Sum(vector<int>& Vector) { // 合計
     int sum_ret = 0;
     vector<int>::iterator ip = Vector.begin();
     
     while(ip != Vector.end()) {
       sum_ret += *ip;
       ip++;
     }
     
     return sum_ret;
   }
   
   float Average(vector<int>& Vector) { // 平均
     int sum = 0;
     float average_ret;
     vector<int>::iterator ip = Vector.begin();
     
     while(ip != Vector.end()) {
       sum += *ip;
       ip++;
     }
     
     average_ret = sum/Vector.size();
     
     return average_ret;
   }
   
   void Display(vector<int>& Vector) { // 要素表示
     vector<int>::iterator ip = Vector.begin();
     
     while(ip != Vector.end()) {
       cout << " " << *ip;
       ip++;
     }
     cout << endl;
     
     return;
   }
  └────────────────────────────────────┘
  ┌────────────────────────────────────┐
   [出力結果]
   合計 : 480
   平均 : 68
   データ個数 : 7
    80 60 50 70 60 90 70
   合計 : 960
   平均 : 68
   データ個数 : 14
    80 60 80 60 50 70 60 90 70 50 70 60 90 70
  └────────────────────────────────────┘
  
  ・main()関数及びローカル関数で定義される反復子ipは、vector<int>テンプレート
   で使用されるiteratorとして定義
されます。
     vector<int>::iterator ip
  
  ・vectorコンテナ内の要素を見るwhileループでは、Vector.end()と同じになるまで
   反復子ipをインクリメント
しています。
  
  ・要素の値については、今回は単純なint型のvectorなので、*ipが値そのものを指し
   ます。
int型ではなくクラスや構造体の場合、然るべきメンバを経由して目的の値
   を取得します。
  
  ・Vector2オブジェクト生成時にはVectorを引数としています。この場合Vector2の要
   素にVectorの要素がコピーされます。同一タイプのオブジェクトを引数にするコン
   ストラクタがvectorには定義されている
ことを意味しています。
  
  ・main関数の中ほどで、反復子ipに2を加えています。これにより、ipの指す要素は
   2個分前進
します。
  
  ・その後insert()メンバ関数で、ip(3番目)の手前に、Vector2の最初から最後までを
   入れています。
この場合もvector2.end()を挿入終了位置に指定している点は注意
   下さい。
  
  ・この例で反復子の概要は把握できたと思います。次回はアルゴリズムを適用してみ
   たいと思います。
  

[Revision Table]
 |Revision |Date    |Comments
 |----------|-----------|-----------------------------------------------------
 |1.00   |2003-08-10 |初版
 |1.01   |2003-08-18 |リンク追加
[end]
Copyright(C) 2003 Altmo