[ PROGRAM ]

C++, C++11

C++ プログラミング

2013/01/26 Tomohiro Kumagai

□ 配列を繰り返し処理する 3 つの方法

C++ では配列を繰り返し処理する方法がいくつか存在します。

ここでは、例えば次の 2 つの配列について、いくつかのパターンで繰り返し処理する方法について見て行きます。

  1. int array[100];
  2. std::vector<int> vector;

最初の配列は C 言語でお馴染みの配列です。

2 番目のものは STL (Standard Template Library) として用意されている std::vector という動的配列です。

 

C++11 の for 構文を使う方法

C++11 で新たに実装された for (:) 構文(範囲に基づく for ループ)を使えるコンパイラでは、それを使った for ループを書くのが簡単です。

C 言語配列の場合

for (int& element : array)

{

 

}

std::vector の場合

for (int& element : vector)

{

 

}

この for : 構文を使うと、":" の右に指定した配列の最初から最後までを順番に、左に指定した変数に取り出して、ブロックの中を順次実行してくれるようになります。

上記のように、左に指定する変数に & を付けて参照型にしておくことで、右に渡した配列から取り出した要素の値をブロック内で書き換えることもできます。

 

面白いのは、C 標準配列と std::vector のどちらでもまったく同じ書き方で繰り返し処理が書けるところです。

std::vector に限らず、std::iterator を返す begin と end とが備わっているクラスであれば、この構文を利用できるのが魅力です。これについての詳しいお話は 独自のクラスを C++11 の範囲に基づく for ループに対応させる に記しておきます。

 

ちなみに注意点としては、for (:) で使う変数は、括弧の中で宣言する必要があるところです。

従来の for (;;) ループであれば、変数を外側で事前に定義しておいてループを抜けた後に最後に設定された値を使うことができましたが、新しい for (:) ではそういうことができません。

ちなみに for (:) の中で変数宣言を忘れると、Xcode 4.5.2 では "For range declaration must declare a variable" というエラーが、Visual Studio 2012 では "宣言が必要です" というエラーが表示されます。

 

従来の for 構文を使う方法

C++11 に対応していないコンパイラの場合は、従来の for (;;) 構文を使って繰り返し処理が行えます。

C 言語配列の場合

for (int i = 0; i < 100; ++i)

{

int& element = array[i];

 

 

}

std::vector の場合

for (int i = 0; i < vector.size(); ++i)

{

int& element = vector[i];

 

 

}

この場合もどちらも書き方はほとんど同じです。

for の最初の項でループカウンタを初期化して、次の項で終了条件としてカウンタが「要素数と一致するまで」とし、ループする度にループカウンタを 1 つ増加させるようにして、ブロック内で現在のインデックス番号の要素を取得して操作します。

終了条件の「要素数と一致するまで」というのは、上記の C 言語配列の場合だと、0 から 99 までの 100 個ということになります。

 

C 言語配列の方だと終了条件で使う要素の個数を自分で管理する必要がありますが、std::vector の場合は size メソッドでいつでも取得できるので簡単です。

 

また、std::vector の場合はイテレータを使って次のようにも書くことができます。

std::vector の場合

std::vector<int>::iterator iterator;

 

for (iterator = vector.begin(); iterator != vector.end(); ++iterator)

{

int& element = *iterator;

 

 

}

適切なイテレータ型の変数(std::vector<T> であれば std::vector<T>::iterator)を準備して、それを begin() メソッドで取得したイテレータで初期化し、end() メソッドで取得できる最後(最終要素の次)を示すイテレータになるまで、イテレータを順次進めています。

この方法を使う方法についての詳細は 可変長配列 std::vector<T> を使用する の方に記してあります。

 

std::for_each を使用する方法

STL (Standard Template Library) をサポートしているコンパイラの場合は、std::for_each を使う方法もあります。

<algorithm> ヘッダーをインクルードしたら、次のようにして std::for_each を使うことができます。

C 言語配列の場合

std::for_each(array, array + 100, [](int& element) {

/* 処理をここに書く */

});

std::vector の場合

std::for_each(vector.begin(), vector.end(), [](int& element) {

/* 処理をここに書く */

});

関数とラムダ関数を使っているため雰囲気が独特ですが、std::for_each の最初の 2 つの引数で、開始位置と終点(最終要素の次)のアドレスやイテレータを指定し、最後の引数で、要素ごとに処理する関数を渡しています。

std::for_each については STL で配列を繰り返し処理する に、ラムダ関数については ラムダ関数を使用する に整理してあるので、詳しくはそちらを参照してください。

 

ただ、ラムダ式は C++11 の機能の一つですので、同じく C++11 の for (:) 構文が使えるコンパイラであれば、このような繰り返し処理は書かない方が判りやすいと思います。

 

逆に C++11 のラムダ式がサポートされていない場合には、各要素毎に処理するための関数を事前に定義しておいて、その関数ポインタを渡すことになります。

void forEachProcess(int& element);

このような関数を定義したとしたら、次のようにして std::for_each を使用できます。

C 言語配列の場合

std::for_each(array, array + 100, &forEachProcess);

std::vector の場合

std::for_each(vector.begin(), vector.end(), &forEachProcess);

関数ポインタの定義方法や指定方法については 関数のポインタを使用する で整理してあります。


[ もどる ]


 

カスタム検索

copyright © Tomohiro Kumagai @ EasyStyle G.K.
contact me: please from mail-form page.