この記事では、C++の標準ライブラリに含まれるstd::array
について詳しく解説します。
std::array
は、固定サイズの配列を効率的に扱うための便利なツールです。
この記事を読むことで、std::array
の基本的な使い方や他の配列との違い、要素の操作方法、関数やSTLアルゴリズムとの連携方法などを学ぶことができます。
初心者の方でも理解しやすいように、具体的なコード例とともに説明していきますので、ぜひ最後までご覧ください。
std::arrayとは
std::arrayの概要
std::array
は、C++標準ライブラリ(STL)の一部であり、固定サイズの配列を扱うためのコンテナです。
C++11で導入され、従来のC言語の配列に比べて多くの利点を持っています。
std::array
は、テンプレートクラスであり、型とサイズをテンプレートパラメータとして受け取ります。
これにより、コンパイル時にサイズが決定され、メモリ効率が向上します。
std::arrayとC++の他の配列との違い
C言語の配列との比較
C言語の配列は、固定サイズの配列を簡単に宣言できる一方で、いくつかの制約があります。
例えば、配列のサイズはコンパイル時に決定され、動的に変更することはできません。
また、配列のサイズを取得するための標準的な方法がなく、ポインタ演算を多用する必要があります。
一方、std::array
は以下の利点を持っています:
- サイズの取得が簡単:
size()メソッド
を使って配列のサイズを簡単に取得できます。 - 範囲チェック:
at()メソッド
を使うことで、範囲外アクセスを防ぐことができます。 - STLとの互換性:
std::array
はSTLの他のコンテナと同様に扱うことができ、STLアルゴリズムと組み合わせて使用できます。
以下に、C言語の配列とstd::array
の基本的な使い方の違いを示します。
#include <iostream>
#include <array>
int main() {
// C言語の配列
int c_array[5] = {1, 2, 3, 4, 5};
std::cout << "C言語の配列のサイズ: " << sizeof(c_array) / sizeof(c_array[0]) << std::endl;
// std::array
std::array<int, 5> cpp_array = {1, 2, 3, 4, 5};
std::cout << "std::arrayのサイズ: " << cpp_array.size() << std::endl;
return 0;
}
std::vectorとの比較
std::vector
は、動的にサイズを変更できる配列コンテナであり、非常に柔軟です。
std::vector
は、要素の追加や削除が頻繁に行われる場合に適しています。
しかし、std::array
には以下の利点があります:
- メモリ効率:
std::array
は固定サイズであるため、メモリ効率が高くなります。 - コンパイル時のサイズチェック:
std::array
のサイズはコンパイル時に決定されるため、サイズに関するエラーを早期に検出できます。 - パフォーマンス:
std::array
は固定サイズであるため、動的メモリ割り当てが不要であり、パフォーマンスが向上します。
以下に、std::array
とstd::vector
の基本的な使い方の違いを示します。
#include <iostream>
#include <array>
#include <vector>
int main() {
// std::array
std::array<int, 5> cpp_array = {1, 2, 3, 4, 5};
std::cout << "std::arrayのサイズ: " << cpp_array.size() << std::endl;
// std::vector
std::vector<int> cpp_vector = {1, 2, 3, 4, 5};
std::cout << "std::vectorのサイズ: " << cpp_vector.size() << std::endl;
// std::vectorに要素を追加
cpp_vector.push_back(6);
std::cout << "std::vectorのサイズ(要素追加後): " << cpp_vector.size() << std::endl;
return 0;
}
このように、std::array
とstd::vector
はそれぞれ異なる用途に適しており、使い分けることで効率的なプログラムを作成することができます。
std::arrayの基本的な使い方
std::arrayの宣言と初期化
基本的な宣言方法
std::array
はテンプレートクラスであり、型とサイズを指定して宣言します。
以下は基本的な宣言方法の例です。
#include <array>
#include <iostream>
int main() {
std::array<int, 5> arr; // int型の要素を5つ持つstd::arrayを宣言
return 0;
}
初期化リストを使った初期化
std::array
は初期化リストを使って初期化することができます。
以下はその例です。
#include <array>
#include <iostream>
int main() {
std::array<int, 5> arr = {1, 2, 3, 4, 5}; // 初期化リストを使って初期化
for (int i : arr) {
std::cout << i << " ";
}
return 0;
}
このコードを実行すると、配列の要素が順に出力されます。
デフォルト初期化
std::array
はデフォルト初期化もサポートしています。
デフォルト初期化では、要素は型に応じたデフォルト値で初期化されます。
#include <array>
#include <iostream>
int main() {
std::array<int, 5> arr = {}; // デフォルト初期化
for (int i : arr) {
std::cout << i << " "; // すべての要素が0で初期化される
}
return 0;
}
要素へのアクセス方法
インデックスを使ったアクセス
std::array
の要素にはインデックスを使ってアクセスできます。
これはC言語の配列と同じ方法です。
#include <array>
#include <iostream>
int main() {
std::array<int, 5> arr = {1, 2, 3, 4, 5};
std::cout << arr[2] << std::endl; // インデックス2の要素にアクセス
return 0;
}
at()メソッドを使ったアクセス
at()メソッド
を使うと、範囲外アクセス時に例外を投げるため、安全に要素にアクセスできます。
#include <array>
#include <iostream>
int main() {
std::array<int, 5> arr = {1, 2, 3, 4, 5};
try {
std::cout << arr.at(2) << std::endl; // インデックス2の要素にアクセス
std::cout << arr.at(5) << std::endl; // 範囲外アクセスで例外が発生
} catch (const std::out_of_range& e) {
std::cerr << "範囲外アクセス: " << e.what() << std::endl;
}
return 0;
}
front()とback()メソッド
front()メソッド
は最初の要素、back()メソッド
は最後の要素にアクセスします。
#include <array>
#include <iostream>
int main() {
std::array<int, 5> arr = {1, 2, 3, 4, 5};
std::cout << "最初の要素: " << arr.front() << std::endl;
std::cout << "最後の要素: " << arr.back() << std::endl;
return 0;
}
サイズの取得
size()メソッド
size()メソッド
を使うと、配列のサイズを取得できます。
#include <array>
#include <iostream>
int main() {
std::array<int, 5> arr = {1, 2, 3, 4, 5};
std::cout << "配列のサイズ: " << arr.size() << std::endl;
return 0;
}
max_size()メソッド
max_size()メソッド
は、配列が持つことのできる最大の要素数を返します。
std::array
の場合、size()
と同じ値を返します。
#include <array>
#include <iostream>
int main() {
std::array<int, 5> arr = {1, 2, 3, 4, 5};
std::cout << "配列の最大サイズ: " << arr.max_size() << std::endl;
return 0;
}
以上が、std::array
の基本的な使い方に関する解説です。
次のセクションでは、std::array
の操作について詳しく見ていきます。
std::arrayの操作
要素の変更
インデックスを使った変更
std::array
の要素を変更する最も基本的な方法は、インデックスを使うことです。
C言語の配列と同様に、インデックスを指定して要素にアクセスし、その値を変更することができます。
#include <iostream>
#include <array>
int main() {
std::array<int, 5> arr = {1, 2, 3, 4, 5};
// インデックスを使って要素を変更
arr[2] = 10;
// 変更後の配列を表示
for (int i = 0; i < arr.size(); ++i) {
std::cout << arr[i] << " ";
}
return 0;
}
このコードを実行すると、配列の3番目の要素が10に変更され、出力は以下のようになります。
1 2 10 4 5
fill()メソッドを使った一括変更
std::array
には、全ての要素を同じ値で一括変更するためのfill()メソッド
があります。
これを使うと、配列全体を簡単に初期化することができます。
#include <iostream>
#include <array>
int main() {
std::array<int, 5> arr = {1, 2, 3, 4, 5};
// fill()メソッドを使って全ての要素を0に変更
arr.fill(0);
// 変更後の配列を表示
for (int i = 0; i < arr.size(); ++i) {
std::cout << arr[i] << " ";
}
return 0;
}
このコードを実行すると、配列の全ての要素が0に変更され、出力は以下のようになります。
0 0 0 0 0
配列のコピーとスワップ
コピーの方法
std::array
はコピー操作が簡単に行えます。
代入演算子を使って、別のstd::array
にコピーすることができます。
#include <iostream>
#include <array>
int main() {
std::array<int, 5> arr1 = {1, 2, 3, 4, 5};
std::array<int, 5> arr2;
// 配列のコピー
arr2 = arr1;
// コピー後の配列を表示
for (int i = 0; i < arr2.size(); ++i) {
std::cout << arr2[i] << " ";
}
return 0;
}
このコードを実行すると、arr1の内容がarr2にコピーされ、出力は以下のようになります。
1 2 3 4 5
swap()メソッド
std::array
には、2つの配列の内容を交換するためのswap()メソッド
も用意されています。
これを使うと、簡単に配列の内容を入れ替えることができます。
#include <iostream>
#include <array>
int main() {
std::array<int, 5> arr1 = {1, 2, 3, 4, 5};
std::array<int, 5> arr2 = {6, 7, 8, 9, 10};
// 配列のスワップ
arr1.swap(arr2);
// スワップ後の配列を表示
std::cout << "arr1: ";
for (int i = 0; i < arr1.size(); ++i) {
std::cout << arr1[i] << " ";
}
std::cout << "\narr2: ";
for (int i = 0; i < arr2.size(); ++i) {
std::cout << arr2[i] << " ";
}
return 0;
}
このコードを実行すると、arr1とarr2の内容が入れ替わり、出力は以下のようになります。
arr1: 6 7 8 9 10
arr2: 1 2 3 4 5
イテレーション
begin()とend()メソッド
std::array
はSTLの他のコンテナと同様に、イテレータを使って要素を順に処理することができます。
begin()メソッド
とend()メソッド
を使って、配列の先頭と末尾を示すイテレータを取得できます。
#include <iostream>
#include <array>
int main() {
std::array<int, 5> arr = {1, 2, 3, 4, 5};
// イテレータを使って配列の要素を表示
for (auto it = arr.begin(); it != arr.end(); ++it) {
std::cout << *it << " ";
}
return 0;
}
このコードを実行すると、配列の全ての要素が順に表示され、出力は以下のようになります。
1 2 3 4 5
range-based forループ
C++11以降では、range-based forループを使って、より簡潔に配列の要素を処理することができます。
#include <iostream>
#include <array>
int main() {
std::array<int, 5> arr = {1, 2, 3, 4, 5};
// range-based forループを使って配列の要素を表示
for (const auto& elem : arr) {
std::cout << elem << " ";
}
return 0;
}
このコードを実行すると、配列の全ての要素が順に表示され、出力は以下のようになります。
1 2 3 4 5
range-based forループを使うことで、コードがより読みやすく、簡潔になります。
std::arrayの応用
多次元配列としてのstd::array
二次元配列の宣言と初期化
std::array
は多次元配列としても利用できます。
例えば、2次元配列を宣言する場合、std::array
をネストして使用します。
#include <array>
#include <iostream>
int main() {
// 3x3の二次元配列を宣言
std::array<std::array<int, 3>, 3> matrix = {{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
}};
// 配列の内容を表示
for (const auto& row : matrix) {
for (const auto& elem : row) {
std::cout << elem << " ";
}
std::cout << std::endl;
}
return 0;
}
このコードでは、3×3の二次元配列を宣言し、初期化リストを使って初期化しています。
多次元配列の操作
多次元配列の要素にアクセスするには、インデックスを2回使用します。
#include <array>
#include <iostream>
int main() {
std::array<std::array<int, 3>, 3> matrix = {{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
}};
// 要素の変更
matrix[1][1] = 10;
// 配列の内容を表示
for (const auto& row : matrix) {
for (const auto& elem : row) {
std::cout << elem << " ";
}
std::cout << std::endl;
}
return 0;
}
この例では、matrix[1][1]の要素を10に変更しています。
std::arrayと関数
std::arrayを引数に取る関数
std::array
を関数の引数として渡すことができます。
以下の例では、std::array
を引数に取る関数を定義しています。
#include <array>
#include <iostream>
// std::arrayを引数に取る関数
void printArray(const std::array<int, 5>& arr) {
for (const auto& elem : arr) {
std::cout << elem << " ";
}
std::cout << std::endl;
}
int main() {
std::array<int, 5> arr = {1, 2, 3, 4, 5};
printArray(arr);
return 0;
}
この関数は、std::array
の内容を表示します。
std::arrayを返す関数
std::array
を返す関数も定義できます。
以下の例では、std::array
を返す関数を定義しています。
#include <array>
#include <iostream>
// std::arrayを返す関数
std::array<int, 5> createArray() {
return {1, 2, 3, 4, 5};
}
int main() {
std::array<int, 5> arr = createArray();
for (const auto& elem : arr) {
std::cout << elem << " ";
}
std::cout << std::endl;
return 0;
}
この関数は、初期化されたstd::array
を返します。
std::arrayとSTLアルゴリズム
std::sort()を使ったソート
std::array
はSTLのアルゴリズムと一緒に使用できます。
以下の例では、std::sort()
を使ってstd::array
をソートしています。
#include <array>
#include <algorithm>
#include <iostream>
int main() {
std::array<int, 5> arr = {5, 3, 4, 1, 2};
// std::sort()を使ってソート
std::sort(arr.begin(), arr.end());
// ソート後の配列を表示
for (const auto& elem : arr) {
std::cout << elem << " ";
}
std::cout << std::endl;
return 0;
}
このコードでは、std::sort()
を使って配列を昇順にソートしています。
std::find()を使った検索
std::find()
を使ってstd::array
内の要素を検索することもできます。
#include <array>
#include <algorithm>
#include <iostream>
int main() {
std::array<int, 5> arr = {5, 3, 4, 1, 2};
// std::find()を使って要素を検索
auto it = std::find(arr.begin(), arr.end(), 3);
if (it != arr.end()) {
std::cout << "Element found at index: " << std::distance(arr.begin(), it) << std::endl;
} else {
std::cout << "Element not found" << std::endl;
}
return 0;
}
このコードでは、std::find()
を使って配列内の要素3を検索し、そのインデックスを表示しています。
要素が見つからない場合は Element not found
と表示されます。
std::arrayの利点と制限
std::arrayの利点
固定サイズによるメモリ効率
std::array
の最大の利点の一つは、その固定サイズによるメモリ効率の良さです。
std::array
はコンパイル時にサイズが決定されるため、動的メモリ割り当てが不要です。
これにより、メモリの断片化を防ぎ、メモリ使用量を最適化することができます。
例えば、以下のコードでは、std::array
を使って固定サイズの配列を宣言しています。
#include <iostream>
#include <array>
int main() {
std::array<int, 5> arr = {1, 2, 3, 4, 5};
for (int i : arr) {
std::cout << i << " ";
}
return 0;
}
このコードでは、std::array
が固定サイズであるため、メモリの効率的な使用が可能です。
コンパイル時のサイズチェック
std::array
のもう一つの利点は、コンパイル時にサイズがチェックされることです。
これにより、配列のサイズに関するエラーを早期に検出することができます。
例えば、以下のコードでは、配列のサイズが不一致であるため、コンパイルエラーが発生します。
#include <array>
int main() {
std::array<int, 5> arr = {1, 2, 3, 4}; // サイズが不一致
return 0;
}
このように、std::array
を使用することで、配列のサイズに関するエラーを防ぐことができます。
std::arrayの制限
サイズの固定
std::array
の最大の制限は、そのサイズが固定されていることです。
一度宣言されたstd::array
のサイズは変更できません。
これは、動的にサイズを変更する必要がある場合には不便です。
例えば、ユーザーからの入力に応じて配列のサイズを変更する場合には、std::vector
の方が適しています。
#include <vector>
#include <iostream>
int main() {
int n;
std::cout << "Enter the size of the array: ";
std::cin >> n;
std::vector<int> vec(n); // 動的にサイズを変更可能
return 0;
}
このように、動的なサイズ変更が必要な場合には、std::vector
を使用する方が適しています。
動的メモリ管理ができない
std::array
は固定サイズであるため、動的メモリ管理ができません。
これは、動的にメモリを割り当てたり解放したりする必要がある場合には不便です。
例えば、大量のデータを扱う場合や、メモリ使用量を最適化する必要がある場合には、std::vector
や他の動的コンテナを使用する方が適しています。
#include <vector>
#include <iostream>
int main() {
std::vector<int> vec;
for (int i = 0; i < 100; ++i) {
vec.push_back(i); // 動的にメモリを割り当て
}
return 0;
}
このように、動的メモリ管理が必要な場合には、std::vector
を使用する方が適しています。
std::array
は固定サイズの配列を効率的に扱うための便利なコンテナです。
固定サイズによるメモリ効率の良さや、コンパイル時のサイズチェックといった利点がありますが、サイズの固定や動的メモリ管理ができないといった制限もあります。
用途に応じて、std::array
と他のコンテナを使い分けることが重要です。
まとめ
この記事では、C++のSTL(Standard Template Library)に含まれるstd::array
について詳しく解説しました。
std::array
は、固定サイズの配列を効率的に扱うための強力なツールです。
この記事を通じて、std::array
の基本的な使い方から応用までを理解し、実際のプログラミングに役立てていただければ幸いです。