HEPtech     - STL (vector, list etc.) -
トップページ | Tips | Topics
<STL (vector, list etc.)>

 ここでは高エネルギーの解析において頻繁に用いられる vector や list などの Standard Template Library (STL) について説明します。

 まずはじめに vector についてですが、これは動的配列などと呼ばれる、配列の長さが後で変えられる配列です。 C++標準の配列は、定義時にconstな定数で配列長を指定する必要がありました。 しかし、この vector を用いる事で、後から追加して長くしたり、削って短くする事ができます。 以下がそのサンプルコードです。

#include <vector>

std::vector<double> vec; // double型の動的配列を定義
vec.push_back(2.1); // 配列の末尾に要素を追加
vec.push_back(1.5);

for ( int i=0;i<vec.size();i++ ){ // size()は要素数を返す
    std::cout << vec[i] << std::endl; // vectorは標準の配列と同様にアクセスできる
}

for ( std::vector<double>::const_iterator i=vec.begin();i!=vec.end();i++ ){
    std::cout << *i << std::endl;
}

vectorを用いるために1行目で対応するヘッダーを呼んでいます。 vectorはstd::vectorのように宣言し、続く括弧には配列の型を指定します。 上の例ではdoubleとあるので、double型の動的配列になるわけです。 指定する型として、intやdoubleの既存の型の他に、クラスを指定することも可能です(後述)。 vectorは、標準の配列と同様に[i]でi番目の要素にアクセスでき、これは後述のlistと異なります。 このアクセス方法とは別に、最後のループのようにイテレータを用いる方法があり、この場合の上下のループの出力結果は同じです。 vectorではイテレータを用いたループで "i!=vec.end()" を "i<vec.end()" とする事も可能なようですが、 次に示すlistとの整合性も考えて上記のように表記しています。

 さて次に list についてですが、これは双方向連結リストとか呼ばれ、まぁ同じく配列の長さが変えられる配列です。 私の理解では、vectorはメモリ上に連続的にある大きさを確保し、順々に要素を追加していき、足りなくなると 配列全体を他所に"コピー"して引き続き追加し、また足りなくなったらコピーして追加というように、 要素数が大きくなるにつれてメモリ上での"コピー"が起こるために遅くなる恐れがあります。 逆に長所としては、配列がメモリ上に連続的に配置しているため、 10番目の要素のみを取り出すなどのランダムアクセスに強いという点があります。 一方 list は追加される都度、各要素がメモリ上にバラバラに配置されるものの、 数字などの内容物を含む1つの要素が次の要素を指しているような構成になっています。 つまり上記で触れた vector とは対照的に処理速度は配列の長さに比較的左右されにくく、 代わりに10番目の要素のみを取り出すなどの操作は不可能で、頭から巡って行かなければなりません。 つまりどちらが優れているという事ではなく、互いに相補的な関係にあるという事です。 以下は先ほどのvectorでの例を list を用いた書いたサンプルコードです。 vector/listという名前と、標準の配列のようにlist[3]のようなアクセスができない点を除き、先ほどと同じコードです。

#include <list>

std::list<double> myList; // double型のlistを定義
myList.push_back(2.1); // 配列の末尾に要素を追加
myList.push_back(1.5);

for ( int i=0;i<myList.size();i++ ){ // size()は要素数を返すが。。。
    //std::cout << myList[i] << std::endl; // listはこのようなアクセスはできない
}

for ( std::list<double>::const_iterator i=myList.begin();i!=myList.end();i++ ){
    std::cout << *i << std::endl;
}

 さて、vectorの所でも少し触れましたが、これらの配列はdouble型の配列などと同様に、あるクラスの配列なんてのも可能です。 以下はカナリ無意味なクラス:myClass型の動的配列を用いた例です。

#include <iostream>
#include <vector>

class myClass{ // サンプル用しょぼいクラス
public:
  myClass(double a, double b):m_p1(a),m_p2(b){;}
  ~myClass(void){;}
  double getSum(void){ return m_p1+m_p2; }
  double getProduct(void){ return m_p1*m_p2; }
  double getDiff(void){ return m_p1-m_p2; }
  double getDiv(void){ return m_p1/m_p2; }
private:
  double m_p1;
  double m_p2;
};

int main(void){
  myClass mc1(2.,3.);
  myClass mc2(12.,3.);
  myClass mc3(2.,13.);
  myClass mc4(6.,4.);
  myClass mc5(16.,3.);
  
  std::vector<myClass> vec; // myClassクラス型の配列
  vec.push_back(mc1);
  vec.push_back(mc2);
  vec.push_back(mc3);
  vec.push_back(mc4);
  vec.push_back(mc5);
  
  for ( std::vector<myClass>::iterator i=vec.begin();i!=vec.end();i++ ){
    std::cout << i->getSum() << ", " << (*i).getDiff() << std::endl;
  }
  
  std::vector<myClass*> vec2; // myClassクラスへのポインタの配列
  for ( int i=0;i<5;i++ ){ vec2.push_back(&vec[i]); }
  
  for ( std::vector<myClass*>::const_iterator i=vec2.begin();i!=vec2.end();i++ ){
    std::cout << (*i)->getProduct() << ", " << (**i).getDiv() << std::endl;
  }
  return 0;
}

上の例では、かなりダミーなmyClassクラスをvectorで扱っています。 例のようにクラスオブジェクトをそのものを要素に持つ事も可能ですし(上半分)、 クラスオブジェクトへのポインタを要素にする事も可能です(下半分)。 forループ内では、それぞれの場合についてクラスのメンバー関数へのアクセスの仕方の例を示しています。 これはvectorなどSTLに限った話ではありませんが、例のように2通りの方法でアクセスできます。

 今回はSTLの vector や list について紹介しました。 これらは実験グループの解析などに携わるようになると目にする機会も増えてくるでしょう。 STLには他にもdeque、mapやsetなど様々な便利なクラスが実装されています。 興味がある人は調べてみると面白いかもしれません。
2005-2017 HEPtech All rights reserved. Link/Unlink free.
inserted by FC2 system