【C++】STLのstd::vectorの使い方について詳しく解説

std::vectorは、可変長配列を扱うためのコンテナであり、非常に使い勝手が良く、処理効率を抜きにすると、すべての配列をstd::vectorに置き換えてもいいレベルの利便性です。

本記事では、初心者でもわかりやすくstd::vectorの基本的な使い方から応用的な使い方まで詳しく解説します。

目次

std::vectorとは何か

std::vectorは、C++のSTL(Standard Template Library)に含まれるコンテナクラスの一つです。std::vectorは、可変長配列を表現するために使用されます。

通常の配列と異なり、std::vectorは要素数を動的に変更できます。

また、要素の追加や削除が容易であり、様々な便利なメソッドが用意されています。

例えば、以下のようにしてstd::vectorを宣言し、初期化することができます。

#include <vector>

int main() {
    // int型の要素を持つ空のstd::vectorを宣言
    std::vector<int> vec1;

    // int型の要素を持ち、初期値が1, 2, 3であるstd::vectorを宣言
    std::vector<int> vec2 = {1, 2, 3};

    return 0;
}

このようにしてstd::vectorを宣言することで、可変長配列を簡単に扱うことができます。

std::vectorの基本的な使い方

要素の追加

std::vectorに要素を追加する方法は、以下の2つがあります。

push_back()関数を使用する方法

std::vectorは、push_back()メソッドを使うことで、末尾に新しい要素を追加することができます。

#include <iostream>
#include <vector>

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

    // 要素を追加する
    vec.push_back(10);
    vec.push_back(20);
    vec.push_back(30);

    // 要素を表示する(要素数はsize()メソッドで取得可能)
    for (int i = 0; i < vec.size(); i++) {
        std::cout << vec[i] << std::endl;
    }

    return 0;
}
10
20
30

insert()関数を使用する方法

末尾ではなく、任意の場所に値を挿入したい場合は、insert()メソッドを使用します。

#include <iostream>
#include <vector>

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

    vec.push_back(10);
    vec.push_back(20);
    vec.push_back(30);

    vec.insert(vec.end(), 40); // 要素を末尾に追加する
    vec.insert(vec.begin() + 0, 50); // 要素を0番目(10)の手前に追加する
    vec.insert(vec.begin() + 3, 60); // 要素を3番目の後ろに追加する

    // 要素を表示する
    for (int i = 0; i < vec.size(); i++) {
        std::cout << vec[i] << std::endl;
    }

    return 0;
}
50
10
20
60
30
40

要素の削除

std::vectorから要素を削除する方法は、以下の2つがあります。

pop_back()関数を使用する方法

pop_back()メソッドは、末尾にある要素を削除するメソッドです。

#include <iostream>
#include <vector>

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

   // 要素を追加する
   vec.push_back(10);
   vec.push_back(20);
   vec.push_back(30);

   // 最後尾の要素を削除する
   vec.pop_back();

   // 要素を表示する
   for (int i = 0; i < vec.size(); i++) {
       std::cout << vec[i] << std::endl;
   }

   return 0;
}
10
20

vecには10, 20, 30の3つの値が追加されていますが、その後にvec.pop_back();が呼ばれているため、末尾にある30が削除され、最終的にvecの中身は{10, 20}となっています。

erase()関数を使用する方法(指定した位置から複数個削除)

erase()メソッドは、指定した位置にある要素、または指定子た範囲の要素を削除するメソッドです。

#include <iostream>
#include <vector>

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

    // 要素を追加する
    for (int i = 1; i <= 5; ++i) {
        vec.push_back(i * 10);
    }
    for (auto&& elem : vec) {
        std::cout << elem << " ";
    }
    std::cout << std::endl;

    // 指定した位置から複数個削除する
    // (3番目から2個なので 30, 40 が消える)
    auto it = begin(vec) + 2;
    auto end_it = it + 2;


    it = vec.erase(it, end_it);

    // 削除後の要素を表示する
    for (auto&& elem : vec) {
        std::cout << elem << " ";
    }

    return 0;
}
10 20 30 40 50
10 20 50

要素のアクセス

std::vector内に格納されたデータにアクセスし、取得したり操作したりできます。 以下はその例です。


#include<iostream>
#include<vector>

using namespace std;

void print(vector<int>& v){
	for(int x:v){
		cout<<x<<" ";
	}
	cout<<endl;
}

int main(){
	vector<int> v={1,2,3,4,5};
	
	print(v);

	//atメソッドでもアクセスできる
	cout << v.at(4) << endl;
	
	return 0;
}
1 2 3 4 5
5

上記コードでは、print関数で全ての要素が出力されます。

ここまでのサンプルコードでもいくつか登場していますが、std::vectorは、通常の配列と同じようにv[0]の形で参照・代入することが可能です。

要素の変更

std::vector内に格納されたデータは、通常の変数と同じように変更可能です。

#include<iostream>
#include<vector>

using namespace std;

void print(vector<int>& v){
	for(int x:v){
		cout<<x<<" ";
	}
	cout<<endl;
}

int main(){
	vector<int> v={1,2,3,4,5};
	
	print(v);
	
	v[3] = 7;
	
	print(v);
	
	return 0;
}
1 2 3 4 5
1 2 3 7 5

上記コードでは、print関数で全ての要素が出力されます。 また、v[3]=7という操作で4番目(実際は3番目)にある数字が7に変更されました。

std::vectorの便利な機能

ここまで、std::vectorの基本的な使い方について解説しましたが、続いてはより高度な機能について紹介します。

イテレータの利用

イテレータとは、コンテナ内の要素を順番にアクセスするためのオブジェクトです。

std::vectorでもイテレータを使用することができます。以下は、イテレータを使用して全要素を出力する例です。

#include <iostream>
#include <vector>

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

    for (auto itr = vec.begin(); itr != vec.end(); ++itr) {
        std::cout << *itr << " ";
    }
    std::cout << std::endl;

    return 0;
}
1 2 3 4 5

上記コードでは、vec.begin()からvec.end()までforループで回しています。また、*itrで現在指している要素を取得しています。

要素のソート

std::vectorでは、要素をソートすることができます。以下は昇順にソートする例です。

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

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

    std::sort(vec.begin(), vec.end());

    for (auto x : vec) {
        std::cout << x << " ";
    }
    std::cout << std::endl;

    return 0;
}
1 2 3 4 5

上記コードでは、std::sort()関数を使用して昇順にソートしています。また、forループ内では範囲for文を使用して全要素を出力しています。

要素の検索

std::vectorでは、要素を検索することができます。以下は値3が存在するかどうか調べる例です。

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

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

    auto itr = std::find(vec.begin(), vec.end(), 3);

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

    return 0;
}
Found: 3

上記コードでは、std:find()関数を使用して値3が存在するかどうか調べています。もし見つかった場合はその値を出力し、Not foundと表示されるようになっています。

std::vectorの注意点

std::vectorは、可変長配列を扱うためのSTLコンテナです。しかし、使用する際にはいくつかの注意点があります。

メモリの確保

std::vectorは、要素数が増えるにつれて自動的にメモリを確保します。

しかし、初期状態ではあまり多くのメモリを確保していないため、大量の要素を追加する場合は、メモリの再確保に時間がかかるため、処理性能が大幅に低下します。

ですが、事前にメモリを確保しておくことで、新たに要素を追加してもメモリの再確保が行われずパフォーナンスを下げずに大量の要素を追加することが可能です。

ですので、事前に必要な要素数を指定してメモリを確保しておくことがおすすめです。

std::vector<int> v(1000); // 要素数1000個分のメモリを事前に確保

//後で変更する場合はresizeメソッドを使う
v.resize(1000000); //100万個分のメモリを事前に確保

要素の削除によるメモリの解放

std::vectorから要素を削除すると、その要素が占めていたメモリ領域は解放されます。

しかし、std::vector自体が確保済みのメモリ領域は解放されません(再利用できるようにするため)。

そのため、大量の要素を削除した後でもメモリ使用量が減らない場合があります。

std::vectorが占有しているメモリのうち、未使用分を解放する場合、resizeメソッドとsizeメソッドを組み合わせることで簡単に行なえます。

std::vector<int> v(1000);

v.resize(v.size()); //現在占有中のメモリ以外を解放する

メモリの再確保を行うため大量のデータを扱う場合は処理が重いですが、無駄に圧迫しているメモリを開放できるので、必要に応じてメモリ解放を行うようにしましょう。

以上が std::vector の注意点です。これらに気を付けて使うことで安全かつ効率的なプログラム作成が可能です。

終わりに

以上が、C++のSTLに含まれるstd::vectorの基本的な使い方や便利な機能、注意点についての解説でした。

std::vectorはC++プログラミングにおいて非常に重要なコンテナであり、上手く活用することで効率的かつ柔軟なプログラムを作成することが出来ます。

是非この記事を参考にして、自分自身のプログラミングスキル向上に役立ててください。

目次