読み込み中...

【C++】vector型の基本から応用まで!初心者も上級者も学べる5選の実例付き解説

C++のvector型を詳しく解説する記事のイメージ C++
この記事は約19分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を満たす現役のプログラマチームによって監修されています。

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

この記事では、C++というプログラミング言語の中で特に重要な部分である「vector型」について詳しく解説します。

C++に初めて触れる方から経験豊かなプログラマまで、幅広く理解できるように、基本から応用までを丁寧に説明していきます。

C++のvector型は、動的配列を扱う際に非常に便利な機能です。

この記事を通じて、C++のvector型の知識を深め、実践的なプログラミングスキルを習得しましょう。

C++は多くの場面で使用される強力なプログラミング言語です。

システムプログラミングからゲーム開発まで、様々な用途に対応しています。

この言語の特徴を理解し、効率的に利用することは、プログラミング能力を高める上で非常に重要です。

そこで、まずはC++とその特徴について簡単に触れ、その後でvector型の詳細に入っていきます。

●C++とは

C++は1979年にベル研究所のバーナード・ストラウストラップによって開発されたプログラミング言語で、C言語の拡張版として登場しました。

C++は、オブジェクト指向プログラミングの概念を取り入れ、複雑なアプリケーションの開発をより簡単にすることを目指しています。

この言語は、高いパフォーマンスと柔軟性を持ち合わせており、システムプログラミング、ゲーム開発、アプリケーションの開発など幅広い分野で利用されています。

○C++の特徴

C++が持つ特徴はそのパワフルさと、多様なプログラミングスタイルをサポートする柔軟性にあります。

高速で効率的な実行が可能なコンパイル言語であり、直接メモリを管理することができるため、システムに近いレベルのプログラミングが可能です。

オブジェクト指向プログラミングをサポートしており、データと機能を一つの単位としてカプセル化することで、再利用性が高く保守しやすいコードを書くことができます。

また、豊富なライブラリとツールが利用できるため、様々なプログラムを効率的に開発することができます。

これらの特徴がC++を多くのプログラマに選ばれる理由となっています。

●vector型とは

C++におけるvector型は、動的にサイズが変更可能な配列を提供する強力なデータ構造です。

従来の配列と異なり、vector型は実行時にサイズを変更でき、要素の追加や削除が柔軟に行えます。

この特性により、プログラマーはコレクションのサイズを事前に知る必要がなく、実行時にデータの量に応じて容量を調整することができます。

vector型は標準テンプレートライブラリ(STL)の一部として提供されており、多くの便利な機能が組み込まれています。

これには、自動的なメモリ管理や、様々なデータ型に対応するテンプレート機能などが含まれています。

また、vectorはイテレータをサポートしているため、STLのアルゴリズムと組み合わせて使用することが可能です。

vector型を使用する際には、#include <vector>を使ってヘッダファイルをインクルードする必要があります。

これにより、vectorクラスのすべての機能が利用可能になります。

○vector型の基本概念

C++におけるvector型は、基本的には動的配列のように機能します。

配列と同様に、vectorにはインデックスを使って個々の要素にアクセスできますが、配列とは異なり、vectorのサイズは実行時に変更が可能です。

例えば、新しい要素を追加するとき、vectorは必要に応じて自動的にメモリを割り当て、サイズを増やします。

vector型は、異なるデータ型の要素を格納するためにテンプレート化されています。

これは、vector<int>vector<string>のように、特定のデータ型の要素を持つvectorを宣言することを意味します。

このテンプレート機能により、様々なタイプのデータに対応する柔軟なコレクションを作成することができます。

また、vectorはコピー操作や範囲ベースのループに対応しています。

これにより、他のvectorから要素をコピーしたり、forループを使って容易に要素を反復処理することが可能です。

○vector型のメリット

vector型を使用することの主なメリットは、その柔軟性と使いやすさにあります。

固定サイズの配列と比較して、vectorは実行時にサイズを変更できるため、より動的なデータ構造を扱う際に便利です。

さらに、vectorは自動的にメモリを管理するため、プログラマーはメモリ割り当てや解放に関する詳細を気にする必要がありません。

vector型は、STLアルゴリズムとシームレスに統合されているため、ソート、検索、変換などの操作を簡単に実行できます。

この統合により、より高度なプログラミングタスクを容易に実装することができ、コードの再利用性と保守性が向上します。

また、vectorはイテレータをサポートしているため、範囲ベースのforループやSTLアルゴリズムと組み合わせて使用することができます。

これにより、vectorの内容を簡単に走査し、処理することが可能です。

●vector型の使い方

C++のvector型の使い方は、初心者にも理解しやすく、かつ多様な機能を備えています。

基本的な操作から始めて、徐々に複雑な操作へと進んでいきます。

vector型を使う際の最初のステップは、vectorオブジェクトの宣言と初期化です。

これは、特定の型の要素を格納するためのvectorを作成することを意味します。

vector型は、さまざまな方法で初期化できます。

例えば、空のvectorを宣言した後に、個別の要素を追加することができます。

また、特定のサイズのvectorを作成し、すべての要素を特定の値で初期化することも可能です。

○サンプルコード1:vectorの初期化

C++でvectorを初期化する基本的な方法は、下記のようになります。

#include <iostream>
#include <vector>

int main() {
    // 空のint型vectorを宣言
    std::vector<int> vec;

    // 初期値を持つvectorを宣言(例: 10個の0)
    std::vector<int> initializedVec(10, 0);

    return 0;
}

この例では、まず空のint型のvectorを宣言しています。

次に、10個の要素を持ち、すべての要素が0で初期化された別のvectorを宣言しています。

これにより、vectorの基本的な初期化方法を理解することができます。

○サンプルコード2:要素の追加

vectorに要素を追加するには、push_backメソッドを使用します。

このメソッドは、新しい要素をvectorの末尾に追加します。

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec;

    // 要素の追加
    vec.push_back(10);
    vec.push_back(20);
    vec.push_back(30);

    // vectorの内容を表示
    for (int i : vec) {
        std::cout << i << std::endl;
    }

    return 0;
}

このコードでは、int型のvectorに3つの要素(10, 20, 30)を追加しています。

forループを使用して、追加された各要素を表示しています。

○サンプルコード3:イテレータを使用したアクセス

vectorの強力な機能の一つに、イテレータのサポートがあります。

イテレータを使用すると、vector内の要素に対してより柔軟なアクセスが可能になります。

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {10, 20, 30, 40, 50};

    // イテレータを使用した要素のアクセス
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        std::cout << *it << std::endl;
    }

    return 0;
}

この例では、イテレータを使用してvector内の各要素にアクセスしています。

begin()メソッドはvectorの最初の要素を指すイテレータを返し、end()メソッドは最後の要素の次を指すイテレータを返します。

このループでは、イテレータを使ってvectorの各要素を順番に処理しています。

○サンプルコード4:要素の削除

C++のvector型では、不要になった要素を効率的に削除することが可能です。

要素の削除は、vectorのサイズと内容に直接影響を与えるため、慎重に行う必要があります。

eraseメソッドを使用して特定の位置の要素を削除したり、clearメソッドでvectorのすべての要素を削除することができます。

下記のサンプルコードでは、特定の要素を削除する方法を表しています。

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {10, 20, 30, 40, 50};

    // 2番目の要素(値が20)を削除
    vec.erase(vec.begin() + 1);

    // vectorの内容を表示
    for (int i : vec) {
        std::cout << i << " ";
    }
    std::cout << std::endl;

    return 0;
}

このコードでは、vec.begin() + 1を使って、vectorの2番目の要素(値が20)を指定し、eraseメソッドで削除しています。

ループを使って、削除後のvectorの内容を表示しています。

○サンプルコード5:vectorのサイズ変更

vector型では、resizeメソッドを使用してサイズを動的に変更することができます。

このメソッドを使うことで、vectorのサイズを増減させることが可能です。

新しいサイズが現在のサイズより大きい場合、新しい要素が追加され、初期化されます。

新しいサイズが現在のサイズより小さい場合、末尾の要素が削除されます。

下記のサンプルコードでは、vectorのサイズを変更する方法を表しています。

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {10, 20, 30, 40, 50};

    // vectorのサイズを3に変更
    vec.resize(3);

    // vectorの内容を表示
    for (int i : vec) {
        std::cout << i << " ";
    }
    std::cout << std::endl;

    return 0;
}

このコードでは、resize(3)を使用してvectorのサイズを3に変更しています。

これにより、元々5つの要素があったvectorから、最後の2つの要素が削除されています。

ループを使って、サイズ変更後のvectorの内容を表示しています。

●vector型の応用例

C++のvector型は、その柔軟性と多機能性から、多くの応用例が存在します。

これらの応用例を通じて、vector型の実用性とその強力な機能を理解することができます。

2次元のvectorの使用から始めて、データのソート、さらにはアルゴリズムとの組み合わせに至るまで、様々なシナリオでvector型を使用する方法を探求します。

○サンプルコード6:2次元vectorの使用

2次元vectorは、表やマトリックスのようなデータ構造を表現するのに適しています。

2次元vectorは、vectorを要素とするvectorとして宣言されます。

#include <iostream>
#include <vector>

int main() {
    // 2次元vectorの宣言(3x4のマトリックス)
    std::vector<std::vector<int>> matrix(3, std::vector<int>(4, 0));

    // 2次元vectorへのアクセスと値の設定
    matrix[1][2] = 5;

    // 2次元vectorの内容を表示
    for (const auto &row : matrix) {
        for (int val : row) {
            std::cout << val << " ";
        }
        std::cout << std::endl;
    }

    return 0;
}

このコードでは、3行4列の2次元vectorを作成し、特定の位置に値を設定しています。

2重のループを使って、matrixの各要素を表示しています。

○サンプルコード7:vectorを用いたデータのソート

vectorと組み込みのソート関数を組み合わせることで、データを効率的にソートできます。

C++の標準ライブラリには、sort関数が用意されており、これを使用してvectorの要素を並べ替えることが可能です。

#include <iostream>
#include <vector>
#include <algorithm> // sort関数のためのヘッダ

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

    // vectorの要素をソート
    std::sort(vec.begin(), vec.end());

    // ソートされたvectorの内容を表示
    for (int val : vec) {
        std::cout << val << " ";
    }
    std::cout << std::endl;

    return 0;
}

このコードでは、std::sort関数を使用してvectorの要素を昇順にソートしています。

ソート後のvectorの内容を表示することで、変更を確認できます。

○サンプルコード8:vectorとアルゴリズムの組み合わせ

vector型は、C++の標準テンプレートライブラリ(STL)のアルゴリズムと組み合わせて使用することができます。

例えば、find関数を使用してvector内の特定の要素を検索することが可能です。

#include <iostream>
#include <vector>
#include <algorithm> // find関数のためのヘッダ

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

    // vector内で値が3の要素を検索
    auto it = std::find(vec.begin(), vec.end(), 3);

    if (it != vec.end()) {
        std::cout << "Found: " << *it << std::endl;
    } else {
        std::cout << "Not found" << std::endl;
    }

    return 0;
}

このコードでは、find関数を使ってvector内で値が3の要素を検索しています。

検索結果はイテレータとして返されます。

このイテレータがvec.end()と等しくない場合、要素が見つかったことを意味し、その値を表示します。

一方、要素が見つからなかった場合には、「Not found」と表示されます。

○サンプルコード9:効率的なメモリ管理

vector型を使う際、効率的なメモリ管理は非常に重要です。

特に大量のデータを扱う場合、不必要なメモリの使用を避け、効率的にリソースを管理することが求められます。

reserveメソッドを使うことで、vectorが予め必要なメモリを確保できるようになり、頻繁なメモリ再割り当てを防ぐことができます。

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec;

    // 事前にメモリを確保
    vec.reserve(100);

    // 大量のデータを追加
    for (int i = 0; i < 100; ++i) {
        vec.push_back(i);
    }

    std::cout << "サイズ: " << vec.size() << std::endl;
    std::cout << "容量: " << vec.capacity() << std::endl;

    return 0;
}

このコードでは、reserveメソッドを使用してvectorの容量を100に設定しています。

その後、100個の要素を追加しています。

これにより、vectorは追加のメモリ再割り当てを行うことなく、効率的にデータを格納できます。

○サンプルコード10:高度なデータ構造の作成

C++のvector型は、高度なデータ構造を作成するのにも適しています。

例えば、vectorを用いてグラフのような複雑なデータ構造を表現することが可能です。

下記のコードは、隣接リストを用いたグラフの表現方法を表しています。

#include <iostream>
#include <vector>

int main() {
    // グラフの隣接リスト表現
    std::vector<std::vector<int>> graph(5);

    // エッジの追加
    graph[0].push_back(1);
    graph[0].push_back(4);
    graph[1].push_back(0);
    graph[1].push_back(3);
    graph[1].push_back(4);
    graph[3].push_back(1);
    graph[4].push_back(0);
    graph[4].push_back(1);

    // グラフの内容を表示
    for (size_t i = 0; i < graph.size(); ++i) {
        std::cout << i << "に接続: ";
        for (int j : graph[i]) {
            std::cout << j << " ";
        }
        std::cout << std::endl;
    }

    return 0;
}

このコードでは、各ノードがどのノードと接続しているかを表す隣接リストをvectorを用いて作成しています。

このような表現方法は、グラフ理論において非常に有用です。

●注意点と対処法

C++でのvector型の使用においては、いくつかの重要な注意点と対処法を理解しておくことが重要です。

これらを適切に把握し、適用することで、より効率的かつ安全にvectorを使用することができます。

主に、メモリの扱い方と性能に関する注意点が挙げられます。

○メモリの扱い方

vector型は動的メモリを使用しており、適切なメモリ管理が必要です。

vectorが自動的にサイズを調整する際、メモリの再割り当てが行われることがあります。

これは、vectorの容量が現在のサイズを超えた場合に発生し、パフォーマンスに影響を与える可能性があります。

このため、vectorの予想される最大サイズがあらかじめ分かっている場合には、reserveメソッドを使用して事前に十分なメモリを確保しておくことが望ましいです。

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec;
    vec.reserve(100); // 100要素分のメモリを予約

    // メモリを予約した後に要素を追加
    for (int i = 0; i < 100; ++i) {
        vec.push_back(i);
    }

    std::cout << "Size: " << vec.size() << std::endl;
    std::cout << "Capacity: " << vec.capacity() << std::endl;

    return 0;
}

このコードでは、vectorに100要素分のメモリを予約しています。

この方法により、後続のpush_back操作においてメモリの再割り当てを防ぐことができます。

○性能上の注意点

vectorの性能に関するもう一つの重要な側面は、要素の追加と削除のコストです。

特に、vectorの先頭や中間に要素を追加・削除する操作は、残りの要素を移動させる必要があるため、コストが高くなります。

可能であれば、要素の追加はvectorの末尾で行うことが望ましいです。

また、不要になった要素を削除する際には、shrink_to_fitメソッドを使用して、vectorの容量を現在のサイズに合わせることで、余分なメモリを解放することができます。

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec(100, 0);

    // 要素の削除
    vec.erase(vec.begin() + 50); // 中間の要素を削除
    vec.shrink_to_fit(); // 容量をサイズに合わせて調整

    std::cout << "Size: " << vec.size() << std::endl;
    std::cout << "Capacity: " << vec.capacity() << std::endl;

    return 0;
}

このコード例では、vectorの中間にある要素を削除した後にshrink_to_fitメソッドを呼び出しています。

これにより、vectorのメモリ使用量を効率的に管理することができます。

まとめ

この記事では、C++のvector型の基本から応用までの使い方を幅広く解説しました。

初心者から上級者まで理解しやすい形で、vectorの初期化、要素の追加、イテレータの使用、要素の削除、サイズの変更、さらには高度なデータ構造の作成までの過程を紹介しました。

また、メモリ管理や性能上の注意点、カスタム型の利用など、vector型を最大限活用するためのカスタマイズ方法も詳しく説明しました。

これらの知識を活用することで、C++プログラミングにおけるデータ構造の効果的な扱い方が身につくことでしょう。