C++の基礎 - STL(array)

提供:MochiuWiki - SUSE, Electronic Circuit, PCB
ナビゲーションに移動 検索に移動

概要

std::arrayは、C++11で追加された固定長配列のコンテナクラスである。
std::vectorに存在するsizeメソッドやbackメソッド等の便利な機能が使用できる。


データ構造

std::arrayは、std::vectorstd::listのようなデータ構造を持たず、通常配列と同じように要素のためにだけメモリを消費する。
std::vectorと比較してメモリ効率が良いが、要素数が多い場合はその効果は期待できない。

std::arrayの交換においては、中身のデータを交換するしかなく、O(N)の処理時間を要する。
std::vector等は、データ領域へのポインタを交換するだけであるため、O(1)の処理時間で済む。


宣言と初期化

宣言

std::arrayは、以下のように宣言する。
要素数は、0以上の整数を指定する。

 #include <array>
 
 std::array<データ型, 要素数> <オブジェクト名>;


以下の例では、int型の要素数10のオブジェクトを宣言している。
宣言のみを行う場合、要素はデフォルト値(整数であれば0)に初期化される。

 #include <array>
 
 std::array<int, 10> ar;     // int型、要素数10


初期化リストによる初期化

初期化リストを使って、各要素を初期化することができる。

 std::array<int, 4> ar{1, 2, 3, 4};     // int型、要素 = {1, 2, 3, 4}


初期化リストの要素数が配列要素数より少ない場合、デフォルト値で初期化される。

 std::array<int, 4> ar{1, 2};     // ar[2], ar[3] は 0 で初期化される。


初期化リストの要素数が配列要素数より多いと、コンパイルエラーとなる。

 std::array<int, 2> ar{1, 2, 3, 4};     // コンパイルエラーとなる


通常の配列であれば、int ar[] = {1, 2, 3, 4};のように要素数を省略できるが、std::arrayでは、要素数を省略できないことに注意すること。

コピーコンストラクタ

コピーコンストラクタとは、同じ型のオブジェクトを渡され、それと同様の内容のオブジェクトを生成するコンストラクタのことである。
std::arrayでは、コピー元オブジェクトと生成するオブジェクトは同じデータ型かつ同一サイズであることに注意すること。

std::array<データ型, サイズ> オブジェクト名(コピー元のオブジェクト名);


以下の例では、オブジェクトXをコピーして、{1, 2, 3}という値をもつオブジェクトYを生成している。

 std::array<int, 3> X{1, 2, 3};
 std::array<int, 3> Y(X);        // コピーコンストラクタ



値の参照と代入

[]演算子(operator[](int))を使用して、通常の配列と同様に、配列要素値の参照・代入が可能である。
ここで、operator[](int)とは、通常の配列の要素にアクセスする時と同様の記述である。(例. ar[10])

以下の例では、配列要素値の参照と代入をしている。(通常の配列要素の参照・代入と同様)

 constexpr int SZ = 10;  // 要素数
 std::array<int, SZ> ar{3, 1, 4, 1, 5, 9, 2, 6, 5, 3};
 
 for (int i = 0; i < SZ; i++)
 {
    std::cout << ar[i];  // arのi番目の要素を表示
 }


 constexpr int SZ = 10;  // 要素数
 std::array<int, SZ> v;  // 指定要素数でstd::arrayを生成
 
 for (int i = 0; i < SZ; i++)
 {
    v[i] = i;            // 要素を 0, 1, 2, 3, ... 9 に設定
 }



メンバ関数

std::arrayの状態を取得

  • bool empty()
  • size_t size()


emptyメソッドは、配列が空かどうかを判定する。
空ならばtrue、空でなければfalseを返す。

これは、後述するsize() == 0と判定するのと同等であるが、コンテナクラスによっては、sizeメソッドよりもemptyメソッドの方が高速な場合があるため、
std::array等のコンテナクラスに対しては、emptyメソッドを使用することが推奨されている。

 if(!ar.empty())
 {  // arが空ではない場合
    // ...処理
 }


sizeメソッドは、要素数を返す関数である。(size_t型は、符号なし整数の組み込み型である)

 for(int i = 0; i != ar.size(); i++)
 {  // 全要素に対するループ
    // ...処理
 }


std::arrayの要素を取得

  • front() : 要素の型への参照
  • back() : 要素の型への参照
  • data() : 要素の型へのポインタ


frontメソッドは、先頭要素への参照を返す。(オブジェクト名[0]と同等)

backメソッドは、末尾要素への参照を返す。(オブジェクト名[オブジェクト名.size() - 1]と同等)

 if(!ar.empty())
 {
    auto f = ar.front();  // 最初の要素
    auto b = ar.back();   // 最後の要素
    // ...略
 }


また、frontメソッドおよびbackメソッドは、先頭または末尾の要素への参照を返すため、先頭または末尾の要素へ代入することもできる。

 std::array<int, 4> ar{1, 2, 3, 4};
 
 ar.front() = 11;  // 最初の要素を 11 に変更
 ar.back() = 44;   // 最後の要素を 44 に変更


dataメソッドは、std::arrayの先頭アドレスを返す。(&オブジェクト名[0]と同等)
std::arrayは、通常配列と同様にデータ領域が連続したアドレスだということが保証されているため、データアドレスを他の関数に渡して処理することもできる。

 std::array<int, 4> ar{1, 2, 3, 4};
 
 int *ptr = ar.data();  // ptrはarの先頭要素へのポインタ
 cout << *ptr << "\n";  // ptrが指す値(1)が表示される


std::arrayの状態を変更

  • void fill(値)
  • void swap()
  • void fill(値)


fillメソッドは、std::arrayの全ての要素を指定した値にする。

 std::array<int, 10> ar;
 
 ar.fill(123);  //  全要素を123に変更する


swapメソッドは、引数で指定されたオブジェクトと内容を入れ替える。 入れ替えるstd::arrayオブジェクトは、型および要素数ともに同一であることに注意する。

 std::array<int, 5> x{1, 2, 3, 4, 5},
                    y{6, 7, 8, 9, 10};
 
 x.swap(y);  // xとyの内容を入れ替える


※注意
std::vectorの場合、オブジェクトが持つデータ領域へのポインタ等を交換するだけであるため、処理時間はO(1)であるが、
std::arrayの場合、要素の全てを交換するため、処理時間はO(N)となる。

イテレータ

  • begin()  : iterator
  • end()  : iterator
  • cbegin() : const_iterator
  • cend()  : const_iterator


beginメソッドとendメソッド
beginメソッドは、最初の要素へのイテレータ、endメソッドは最後の要素の次へのイテレータを返す。

 int i = 0;
 for(auto itr = ar.begin(); itr != ar.end(); itr++, i++)
 {
    *itr = i;
 }


cbeginメソッドとcendメソッド
cbeginメソッドは最初の要素へのconstイテレータ、cendメソッドは最後の要素の次のconstイテレータを返す。
通常のイテレータの場合、*itr = 123;のように指している先の値を変更できるが、constイテレータの場合、*itr = 123;のように記述するとコンパイルエラーとなる。

 for(auto itr = ar.cbegin(); itr != ar.cend(); ++itr)
 {
    // *itr = 123;    //  const イテレータの指す先への代入はコンパイルエラーとなる
    std::cout << *itr << "\n";
 }



アルゴリズム

C++には便利なアルゴリズムがいくつも用意されている。
http://www.cplusplus.com/reference/algorithm/

std::arrayに対してよく使用されるアルゴリズムを、以下に記載する。

  • accumulate(first, last, 初期値) : 要素型
  • void sort(first, last)
  • void reverse(first, last)


accumulate(first, last, 初期値)

accumulate(<オブジェクト名>.begin(), <オブジェクト名>.end(), init)メソッドは、コンテナの最初から最後までを要素を積算(全加算)する。
第3引数は、初期値(通常は0)を指定する。

なお、accumulateメソッドを使用するには、#include <numeric>が必要である。

 #include <numeric>
 
 int main()
 {
    std::array<int, 10> v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    std::cout << std::accumulate(v.begin(), v.end(), 0) << "\n";
 }


一部だけを積算する場合、accumulate(v.begin() + i, v.begin() + k, 0)のように、イテレータで範囲を指定する。
また、accumulate(&v[i], &v[k], 0)のように、要素へのポインタで指定することも可能である。

sort(first, last)

sort(<オブジェクト名>.begin(), <オブジェクト名>.end())メソッドは、コンテナの要素を昇順にソートする。
なお、sortメソッドを使用するには、#include <algorithm>が必要である。

 #include <algorithm>
 
 int main()
 {
    std::array<int, 11> v{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
    std::sort(v.begin(), v.end());
 
    return 0;
 }


一部をソートする場合、sort(v.begin() + i, v.begin() + k)のように、イテレータで範囲を指定する。
また、sort(&v[i], &v[k])のように、要素へのポインタで指定することも可能である。

reverse(first, last)

reverse(<オブジェクト名>.begin(), <オブジェクト名>.end())メソッドは、コンテナの要素を逆順にする。

 std::array<int, 11> v{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
 
 std::reverse(v.begin(), v.end());  // 順序反転
 
 for(auto x : v)
 {
    std::cout << x << " ";
 }
 
 std::cout << "\n";