arrayクラス

配列に代わる機能1

C言語では同じデータ型の変数をたくさん扱う場合には配列を使用します。
C++でも配列は使用しますが、データの集合をより便利に扱えるコンテナクラス(コンテナ型)を使用することが多いです。

コンテナクラスはSTL(Standard Template Library)と呼ばれるものの一部です。
STLはテンプレート機能(関数のテンプレートと同じ)を利用したライブラリです。

コンテナ型のひとつであるarrayクラスは基本的に通常の配列と同じ物ですが、配列よりも便利な機能が用意されています。
記述方法さえ覚えてしまえばほとんど同じものとして扱えます。

arrayクラスは比較的最近追加された機能で、古いC++コンパイラでは対応していない場合があります。
Visual Studio 2015では使用可能です。


#include <iostream>
#include <array>

int main()
{
    std::array<int, 5> arr{1, 2, 3, 4, 5};
    for (int i = 0; i < arr.size(); i++)
    {
        std::cout << arr[i] << "\n";
    }

    std::cin.get();
}

arrayクラスを使用するには#include <array>を記述します。
arrayクラスは名前空間stdの中に存在します。

宣言と初期化

arrayクラスの使用の宣言は以下のようにします。


std::array<int, 5> arr;

これは「要素数が5のint型」のarrayクラスの使用を宣言しています。
宣言だけで初期化をしない場合、それぞれの要素の値は不定です。

宣言と同時に初期化するには以下のようにします。


std::array<int, 5> arr1{ 1, 2, 3, 4, 5 };
std::array<int, 5> arr2 = { 1, 2, 3, 4, 5 };

//足りない分は0で埋められる
std::array<int, 5> arr3{ 1, 2 }; 

//以下はエラー

//初期化子が多すぎる
std::array<int, 5> arr4{ 1, 2, 3, 4, 5, 6 };

//要素数の省略はできない
std::array<int> arr5{ 1, 2, 3, 4, 5 };

要素数の指定に対して初期化子が少ないと、残りは0で埋められます。
要素数の指定に対して初期化子が多すぎるとエラーになります。
これらは配列と同じです。

配列と違うのは、初期化子を指定しても要素数の記述を省略できない点です。
配列と比べてこれはわずかに不便と言えるかもしれません。

また、別のarrayクラスを利用して初期化することもできます。


std::array<int, 5> arr1{ 1, 2, 3, 4, 5 };

//arr1でarr2を初期化
std::array<int, 5> arr2(arr1);
//↓もOK
//std::array<int, 5> arr2 = arr1;

//コピーなので元が変更されても影響しない
arr1[0] = 10;

//1を表示
std::cout << arr2[0];

この方法では、arr1の各要素がarr2にそのままコピーされます。
この時、初期化に利用できるのはデータ型と要素数が同じarrayクラスに限られます。

ちなみに、あるクラスの使用を宣言し、使えるようにしたものをインスタンスといいます。
上のコードではarr1やarr2などがインスタンスです。
これはクラスの項で改めて説明します。

代入操作

array同士はそのまま代入によるコピーが可能です。


#include <iostream>
#include <array>

int main()
{
    std::array<int, 5> arr1{ 1, 2, 3, 4, 5 };
    std::array<int, 5> arr2;

    //arr2にarr1の要素をすべてコピー
    arr2 = arr1;

    //コピー元を書き換えてもコピー先に影響しない
    arr1[0] = 10;
    std::cout << arr2[0] << std::endl;

    std::cin.get();
}

全ての要素がコピーされますので、コピー元を書き換えてもコピー先には影響しません。

ただし、代入できるのはデータ型と要素数が同一のarrayオブジェクト同士のみです。

各要素へのアクセス

各要素の値の取り出し、値の代入方法は配列と同じです。


#include <iostream>
#include <array>

int main()
{
    std::array<int, 5> arr{ 1, 2, 3, 4, 5 };

    arr[1] = 10;

    //10
    std::cout << arr[1] << std::endl;

    std::cin.get();
}

特に難しい点はないでしょう。

strngクラスの時と同様に、より安全なat関数によるアクセス方法も存在します。
しかしarrayクラスでは使用するメリットはあまりありません。

二次元配列

二次元配列として使用する場合は以下のようにします。


#include <iostream>
#include <array>

int main()
{
    int nums[2][3] = {
        { 1, 2, 3 },
        { 4, 5, 6 }
    };

    //↑と同等
    std::array<std::array<int, 3>, 2> arr{ {
        { 1, 2, 3 },
        { 4, 5, 6 }
    } };

    for (int i = 0; i < arr.size(); i++)
    {
        for (int j = 0; j < arr[i].size(); j++)
        {
            std::cout << arr[i][j] << " ";
        }
        std::cout << "\n";
    }

    std::cin.get();
}

宣言/初期化時に注意点がふたつ。
配列の場合とは要素数の並び順が逆転します。
初期化子は配列の場合の初期化子を、さらに波括弧で括る必要があります。

配列と比べて「using namespace std;」を使用してもコード記述量が少し多く、また要素の並び順がやや直感的でないのが難点です。

関数

arrayクラスには便利な関数が多数存在します。
その中でよく使いそうなものをいくつかピックアップします。

以下の関数は他のコンテナ型にも共通して存在するものがあります。
(ただし引数の指定が異なることがあります)

size関数

arrayの要素数を得るにはsize関数を使用します。


#include <iostream>
#include <array>

int main()
{
    std::array<int, 5> arr{ 1, 2, 3, 4, 5 };
    for (int i = 0; i < arr.size(); i++)
    {
        std::cout << arr[i] << "\n";
    }

    std::cin.get();
}

通常の配列の場合は「sizeof(arr) / sizeof(arr[0])」という、やや面倒な書式を使用する必要がありましたが、簡単に取得できるようになっています。
ちなみに戻り値は「size_t型」です。
(VC++ではunsigned ing型)

empty関数

arrayが空か否かを判定するにはempty関数を使用します。


#include <iostream>
#include <array>

int main()
{
    std::array<int, 0> arr1;
    std::array<int, 5> arr2{ 1, 2, 3, 4, 5 };

    std::cout << arr1.empty() << std::endl;
    std::cout << arr2.empty() << std::endl;

    std::cin.get();
}

空の場合は0を、それ以外の場合は0以外を返します。

空のarrayクラスを宣言することはほとんどないので使い道はありませんが、他のコンテナ型ではあり得ることで、統一性を持たせるために用意されています。

bool型

実はempty関数の戻り値はbool型というものです。
これはC++から導入された新しいデータ型です。

bool型は「真」または「偽」のどちらかの状態を持つ型です。
真の状態をtrue、偽の状態をfalseといいます。

falseは0と等価で、trueは0以外を表します。
そのため、if文などの条件判定ではそのまま記述することができます。


std::array<int, 0> arr;

if (arr.empty())
	std::cout << "空です。" << std::endl;
else
	std::cout << "空ではないです。" << std::endl;

front関数、back関数

array要素の取得、設定にはfront関数back関数を使用する方法もあります。


#include <iostream>
#include <array>

int main()
{
    std::array<int, 5> arr{ 1, 2, 3, 4, 5 };

    std::cout << arr.front() << std::endl;
    std::cout << arr.back() << std::endl;

    //値の書き換えもできる
    arr.back() = 50;

    std::cout << arr.back() << std::endl;

    std::cin.get();
}

back関数は要素数に関わらず常に最後の要素にアクセスできるので便利です。
front関数は「arr[0]」と同じことなのであまり使い道はありませんが、書式を合わせるために存在します。

fill関数

fill関数はすべての要素に同じ値をセットします。


#include <iostream>
#include <array>

int main()
{
    std::array<int, 5> arr;

    arr.fill(10);

    //「10」を5個表示
    for (int i = 0; i < arr.size(); i++)
    {
        std::cout << arr[i] << "\n";
    }

    std::cin.get();
}

fill関数は他のコンテナ型には存在しないので、代わりにassign関数を使用します。
arrayクラスにおいてはfill関数もassign関数も同じです。

swap関数

swap関数は、二つのarrayオブジェクト同士の要素をそっくり入れ替える関数です。


#include <iostream>
#include <array>

int main()
{
    std::array<int, 3> arr1{ 1, 2, 3 };
    std::array<int, 3> arr2{ 4, 5, 6 };

    arr1.swap(arr2);

    //4 5 6
    for (int i = 0; i < arr1.size(); i++)
    {
        std::cout << arr1[i] << " ";
    }
    std::cout << "\n";

    //1 2 3
    for (int i = 0; i < arr2.size(); i++)
    {
        std::cout << arr2[i] << " ";
    }

    std::cin.get();
}

swap関数を使用するには、両方のarrayオブジェクトのデータ型と要素数が同じである必要があります。

また、swap関数は標準関数としても用意されており、これは通常の配列にも利用することができます。


int arr1[] = { 1, 2, 3 };
int arr2[] = { 4, 5, 6 };

std::swap(arr1, arr2);