コンテンツにスキップ

C++/標準ライブラリ/vector

出典: フリー教科書『ウィキブックス(Wikibooks)』

<vector>の概要

[編集]

std::vectorとは

[編集]

ヘッダー<vector>ではstd::vectorが定義されています。 std::vectorは、C++標準ライブラリで提供される動的配列を表すコンテナです。要素の追加や削除が可能であり、可変長の配列として使用されます。要素は連続したメモリ領域に格納され、インデックスによるランダムアクセスが高速に行える特徴があります。

使用例

[編集]
#include <iostream>
#include <vector>

auto main() -> int {
    // int型のvectorを作成し、初期化
    std::vector<int> numbers = {1, 2, 3, 4, 5};

    // 要素の追加
    numbers.push_back(6);

    // 要素の削除
    numbers.pop_back();

    // イテレーションを使用した要素の表示
    std::cout << "Numbers: ";
    for (auto num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    // インデックスを使用した要素の表示
    std::cout << "First element: " << numbers[0] << std::endl;

    return 0;
}

この例では、std::vectorを使用してint型の動的配列を作成し、要素の追加、削除、イテレーション、およびインデックスを使用した要素のアクセス方法を示しています。

std::vector のメソッド
メソッド 説明
begin() 先頭要素へのイテレータを返す。
end() 末尾の次の要素へのイテレータを返す。
size() ベクタの要素数を返す。
capacity() 現在のメモリ割り当ての容量を返す。
empty() ベクタが空かどうかを判定する。
push_back(const T& value) ベクタの末尾に要素を追加する。
push_back(T&& value) ムーブセマンティクスを使用して要素を追加する。
pop_back() ベクタの末尾の要素を削除する。
clear() ベクタの全要素を削除し、サイズを0にする。
resize(size_type count) ベクタのサイズを変更する。
resize(size_type count, const T& value) ベクタのサイズを変更し、新しい要素を指定した値で初期化する。
reserve(size_type new_capacity) メモリの容量を指定した値に増やす。
shrink_to_fit() 余分なメモリを解放し、容量を要素数に合わせる。
at(size_type pos) 指定した位置の要素にアクセスする。範囲外アクセスをチェックする。
operator[](size_type pos) 指定した位置の要素にアクセスする。範囲外アクセスは未定義の動作を引き起こす。
front() 先頭要素にアクセスする。
back() 末尾の要素にアクセスする。
data() 内部バッファの先頭へのポインタを返す。
swap(vector& other) 他のベクタと要素を交換する。

これらのメソッドを使用することで、std::vectorクラスのインスタンスを操作し、要素の追加、削除、アクセスなどの処理を行うことができます。

std::vectorの基本的な操作

[編集]

std::vectorの作成

[編集]

std::vectorを作成する方法には、いくつかの方法があります。

デフォルトコンストラクタを使用する方法
#include <vector>

auto main() -> int {
    // デフォルトコンストラクタを使用して空のvectorを作成
    std::vector<int> numbers;

    return 0;
}
サイズ指定のコンストラクタを使用する方法
#include <vector>

auto main() -> int {
    // サイズ5のvectorを作成し、全ての要素を0で初期化
    std::vector<int> numbers(5);

    return 0;
}
初期化子リストを使用する方法
#include <vector>

auto main() -> int {
    // 初期化子リストを使用して要素を初期化
    std::vector<int> numbers = {1, 2, 3, 4, 5};

    return 0;
}

要素の追加と削除

[編集]
要素の追加
push_back()メソッドを使用する方法
#include <vector>

auto main() -> int {
    std::vector<int> numbers = {1, 2, 3};
    numbers.push_back(4); // 要素4を追加

    return 0;
}
要素の削除
pop_back()メソッドを使用する方法
#include <vector>

auto main() -> int {
    std::vector<int> numbers = {1, 2, 3};
    numbers.pop_back(); // 最後の要素を削除

    return 0;
}

要素へのアクセス

[編集]
インデックスを使用する方法
#include <iostream>
#include <vector>

auto main() -> int {
    std::vector<int> numbers = {1, 2, 3};
    std::cout << "First element: " << numbers[0] << std::endl;

    return 0;
}
at()メソッドを使用する方法
#include <iostream>
#include <vector>

auto main() -> int {
    std::vector<int> numbers = {1, 2, 3};
    std::cout << "First element: " << numbers.at(0) << std::endl;

    return 0;
}

at()メソッドを使用すると、範囲外のインデックスへのアクセス時に例外がスローされます。

std::vectorの操作とアルゴリズム

[編集]

ソート

[編集]

std::vectorの要素をソートする方法には、さまざまなアルゴリズムがありますが、std::sort関数を使用する方法が一般的です。

#include <iostream>
#include <vector>
#include <algorithm>

auto main() -> int {
    std::vector<int> numbers = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3};
    
    // デフォルトの昇順ソート
    std::sort(numbers.begin(), numbers.end());

    // ソート後の要素を出力
    std::cout << "Sorted numbers: ";
    for (auto num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

検索

[編集]

std::vector内の要素を検索する方法には、std::find関数やstd::binary_search関数を使用する方法があります。

#include <iostream>
#include <vector>
#include <algorithm>

auto main() -> int {
    std::vector<int> numbers = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3};
    
    // 要素の検索
    int target = 5;
    auto it = std::find(numbers.begin(), numbers.end(), target);
    if (it != numbers.end()) {
        std::cout << "Found " << target << " at index " << std::distance(numbers.begin(), it) << std::endl;
    } else {
        std::cout << target << " not found" << std::endl;
    }

    // ソートされた要素の検索
    std::sort(numbers.begin(), numbers.end());
    if (std::binary_search(numbers.begin(), numbers.end(), target)) {
        std::cout << "Found " << target << " (sorted)" << std::endl;
    } else {
        std::cout << target << " not found (sorted)" << std::endl;
    }

    return 0;
}

変更

[編集]

std::vectorの要素を変更する方法には、インデックスを指定して要素にアクセスし、新しい値で要素を上書きする方法があります。

#include <iostream>
#include <vector>

auto main() -> int {
    std::vector<int> numbers = {3, 1, 4, 1, 5};
    
    // 要素の変更
    numbers[2] = 9; // 3番目の要素を9に変更

    // 変更後の要素を出力
    std::cout << "Changed numbers: ";
    for (auto num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

std::vectorのイテレータ

[編集]

イテレータの概要

[編集]

イテレータは、コンテナ内の要素を反復処理するためのポインタのようなオブジェクトです。イテレータを使用することで、コンテナの要素に順次アクセスし、変更することができます。

イテレータの使用法

[編集]

std::vectorのイテレータは、begin()end()メソッドを使用して取得できます。begin()メソッドはコンテナの先頭要素を指すイテレータを返し、end()メソッドはコンテナの末尾の次の要素を指すイテレータを返します。これらのメソッドを使用して、範囲ベースのループやアルゴリズムを実装することができます。

#include <iostream>
#include <vector>

auto main() -> int {
    std::vector<int> numbers = {1, 2, 3, 4, 5};

    // イテレータを使用した範囲ベースのループ
    std::cout << "Using iterators: ";
    for (auto it = numbers.begin(); it != numbers.end(); it++) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;

    // イテレータを使用した要素の変更
    for (auto it = numbers.begin(); it != numbers.end(); it++) {
        *it *= 2;
    }

    // 変更後の要素を出力
    std::cout << "Changed numbers: ";
    for (auto num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

この例では、イテレータを使用してstd::vectorの要素を反復処理し、要素を変更する方法を示しています。

std::vectorの特殊化と拡張

[編集]

bool型の特殊化

[編集]

カスタムアロケータの使用

[編集]

std::vectorの特殊化と拡張

[編集]

bool型の特殊化

[編集]

通常、std::vectorは要素を連続したメモリ領域に格納するため、1ビットごとに要素を格納するbool型の特殊化があります。これにより、メモリ使用量が最適化され、効率的なブールベクタが実現されます。

#include <iostream>
#include <vector>

auto main() -> int {
    std::vector<bool> bits = {true, false, true, false, true};

    // イテレーションを使用した要素の表示
    std::cout << "Bits: ";
    for (bool bit : bits) {
        std::cout << bit << " ";
    }
    std::cout << std::endl;

    return 0;
}

カスタムアロケータの使用

[編集]

std::vectorは通常、デフォルトのアロケータを使用してメモリを管理しますが、必要に応じてカスタムアロケータを使用することもできます。これにより、アプリケーションのメモリ管理をカスタマイズし、パフォーマンスを向上させることができます。

#include <iostream>
#include <vector>

// カスタムアロケータの例
template <typename T>
struct MyAllocator {
    using value_type = T;

    T* allocate(std::size_t n) {
        return static_cast<T*>(std::malloc(n * sizeof(T)));
    }

    void deallocate(T* p, std::size_t) noexcept {
        std::free(p);
    }
};

auto main() -> int {
    // カスタムアロケータを使用してvectorを作成
    std::vector<int, MyAllocator<int>> numbers = {1, 2, 3, 4, 5};

    // 要素の表示
    std::cout << "Numbers: ";
    for (auto num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

この例では、MyAllocatorという名前の独自のアロケータを定義し、それを使用してstd::vectorを作成しています。

パフォーマンスと最適化

[編集]

メモリ管理

[編集]

std::vectorのパフォーマンスとメモリ管理は密接に関連しています。メモリ管理に関する最適化のいくつかの考慮事項は以下の通りです。

リサイズの最適化
ベクタは動的なサイズ変更が可能ですが、リサイズが頻繁に行われるとパフォーマンスに影響を与える可能性があります。特に大量の要素を追加する場合は、容量の事前確保を行うことでリサイズの回数を減らし、パフォーマンスを向上させることができます。
要素のコピーの最適化
std::vectorの要素は通常、コピーされます。要素が大きい場合や、コピーが頻繁に発生する場合は、ムーブセマンティクスや参照を使用して不要なコピーを避けることでメモリ使用量を最適化できます。

アルゴリズムの選択

[編集]

std::vectorを操作する際に使用するアルゴリズムの選択は、パフォーマンスに大きな影響を与えます。適切なアルゴリズムを選択することで、処理速度を向上させることができます。

ソートアルゴリズムの選択
ソート操作を行う場合、std::sort()関数を使用することが一般的です。しかし、要素数が少ない場合は挿入ソートやバブルソートのような単純なアルゴリズムの方が効率的かもしれません。
検索アルゴリズムの選択
要素の検索を行う場合、std::find()関数を使用することが一般的ですが、ソートされたベクタではバイナリサーチが効率的です。適切なアルゴリズムを選択し、処理の時間計算量を最小限に抑えることが重要です。
変更操作の最適化
要素の変更操作を行う場合、ムーブセマンティクスやメモリアロケーションの最適化を行うことで、パフォーマンスを向上させることができます。

適切なメモリ管理とアルゴリズムの選択は、パフォーマンスを最適化し、効率的なプログラムを実装する上で重要な要素です。

実践的な応用

[編集]

std::vectorの実践的な応用

[編集]
データの管理
std::vectorは可変長の配列を提供するため、さまざまなデータ管理の応用に使用されます。例えば、動的なデータの格納や操作、動的なサイズのデータの処理などに利用されます。特に、要素の追加や削除が頻繁に発生する場合や、要素の数が動的に変化する場合に有用です。
アルゴリズムの実装
std::vectorはアルゴリズムの実装に広く使用されます。例えば、数値計算やデータ処理のアルゴリズム、グラフや木構造の操作など、さまざまな問題に対する解法に使用されます。std::vectorを使用することで、データの保持と操作を効率的に行うことができます。
コンテナの実装
std::vectorはコンテナの実装にも使用されます。例えば、スタックやキューなどの抽象データ型を実装する際に、内部的にstd::vectorを使用することで、データの追加や削除を効率的に行うことができます。また、カスタムコンテナの実装やライブラリの開発にも活用されます。
データ構造の実装
std::vectorはさまざまなデータ構造の実装に使用されます。例えば、動的配列、動的スタック、動的キューなど、様々なデータ構造を実装する際に使用されます。std::vectorを使用することで、データの格納と操作を効率的に行うことができます。

std::vectorはその柔軟性と効率性から、さまざまな実践的な応用に広く活用されています。データの管理からアルゴリズムの実装、さらにはデータ構造の実装まで、幅広い領域で利用されています。

その他の関連トピック

[編集]

ハッシュサポート

[編集]

std::vectorは、ハッシュサポートも提供しています。特定のハッシュ関数を使用してstd::vectorの要素をハッシュすることができます。これにより、要素の検索や一意の識別が効率的に行われます。例えば、std::unordered_setstd::unordered_mapなどのハッシュテーブルを実装する際に使用されます。

#include <iostream>
#include <vector>
#include <unordered_map>

auto main() -> int {
    std::vector<int> numbers = {1, 2, 3, 4, 5};

    // ハッシュマップの作成
    std::unordered_map<int, int> hashMap;
    for (auto num : numbers) {
        hashMap[num] = num * num; // 要素をキーとして格納
    }

    // ハッシュマップの要素の表示
    for (const auto& pair : hashMap) {
        std::cout << "Key: " << pair.first << ", Value: " << pair.second << std::endl;
    }

    return 0;
}

C++標準ライブラリのポインタ化

[編集]

C++標準ライブラリは、ポインタを使用して効率的にデータを管理するための機能も提供しています。ポインタを使用することで、データへの直接のアクセスやメモリの効率的な管理が可能になります。std::vectorに限らず、他のコンテナやデータ構造もポインタを使用して効率的なデータの管理を行うことができます。

#include <iostream>
#include <vector>

auto main() -> int {
    // int型のポインタを使用してvectorを作成
    std::vector<int*> ptrVector;

    // ポインタをvectorに追加
    int num1 = 10, num2 = 20, num3 = 30;
    ptrVector.push_back(&num1);
    ptrVector.push_back(&num2);
    ptrVector.push_back(&num3);

    // ポインタを使用して要素にアクセス
    for (int* ptr : ptrVector) {
        std::cout << *ptr << " ";
    }
    std::cout << std::endl;

    return 0;
}

このように、C++標準ライブラリはポインタを使用してデータを効率的に管理するためのさまざまな機能を提供しています。ポインタを使用することで、メモリの効率的な使用やデータへの直接のアクセスが可能になります。


[[Category:C++|へくた}}