C++でvectorを使う

もちろんC++ Builder CEでも使えますし、黙って使っていましたが、一度基礎から扱います

さて、vector(ベクターないしベクトル)は、

C++標準ライブラリ(<vector>)で提供される、サイズを動的に変更できる可変長配列のコンテナクラスです。生の配列とは異なり、要素の追加や削除に応じてメモリ領域が自動で管理されます。

添え字(index)で扱えるという意味では配列に似ています。ヘッダーをインクルードすると使えます。例を挙げましょう。

#include <iostream>
#include <vector>

int main() {
    // ベクトルの宣言と初期化
    std::vector<int> numbers;

    // 末尾へ要素を追加する
    numbers.push_back(10);
    numbers.push_back(20);
    numbers.push_back(30);

    // 要素数(size)の取得
    std::cout << "要素数: " << numbers.size() << std::endl;

    // 添字(インデックス)で要素にアクセス
    std::cout << "1番目の要素: " << numbers[0] << std::endl;

    // 範囲ベースfor文で全要素を走査
    std::cout << "要素一覧: ";
    for (int n : numbers) {
        std::cout << n << " ";
    }
    std::cout << std::endl;

    return 0;
}

要素がintなvectorを宣言していますが、それなら配列を使えばいいので、ユーザが定義した型のインスタンスを入れるコンテナとしてvectorを使いましょう。さらにそれをソートします。

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

struct Person {
    int id;
    std::string name;
};
int main() {
    std::vector<Person> people = {{2,"Bob"}, {1,"Alice"}, {3,"Charlie"}};
    std::sort(people.begin(), people.end(), [](const Person &a, const Person &b){
        return a.id < b.id; // idで昇順
    });
    for( int i = 0 ; i < people.size() ; i++ )
	std::cout << people[i].id << " " << people[i].name << std::endl; 
}
struct Person {
    int id;
    std::string name;
};

Personというユーザ定義の型(構造体)です。これを宣言してさらに初期化(要素数は3)するコードが、

    std::vector<Person> people = {{2,"Bob"}, {1,"Alice"}, {3,"Charlie"}};

です。

次でソートしていますが、ラムダ式([])が分かりにくいですね。ラムダ式は関数を”そこに”表現するC++の記法で、慣れれば別関数として書くより可読性が高くなります。

    std::sort(people.begin(), people.end(), [](const Person &a, const Person &b){
        return a.id < b.id; // idで昇順
    });

降順ならば、

return a.id > b.id;

です。一度C++ Builder CEのc++ コンパイラで動かして見ましょう。

jakeb@Orbit-11 MINGW64 ~/Dropbox/vector-sort
$ cat main.cpp
#include <vector>
#include <algorithm>
#include <string>
#include <iostream>

struct Person {
    int id;
    std::string name;
};
int main() {
    std::vector<Person> people = {{2,"Bob"}, {1,"Alice"}, {3,"Charlie"}};
    std::sort(people.begin(), people.end(), [](const Person &a, const Person &b){
        return a.id < b.id; // id
    });
    for( int i = 0 ; i < people.size() ; i++ )
        std::cout << people[i].id << " " << people[i].name << std::endl;
}

jakeb@Orbit-11 MINGW64 ~/Dropbox/vector-sort
$ bcc32c main.cpp
Embarcadero C++ 7.70 for Win32 Copyright (c) 2012-2024 Embarcadero Technologies, Inc.
main.cpp:
Turbo Incremental Link 6.99 Copyright (c) 1997-2024 Embarcadero Technologies, Inc.

jakeb@Orbit-11 MINGW64 ~/Dropbox/vector-sort
$ ./main
1 Alice
2 Bob
3 Charlie

vectorの全要素を表示するループは、

    for( int i = 0 ; i < people.size() ; i++ )
        std::cout << people[i].id << " " << people[i].name << std::endl;

people.size()は要素数を返します。従って先頭要素から最終要素までをダンプするコードは上のようになります。people[i]で各要素がアクセスできていることが理解できます。んでも、構造体の配列でも同じですね。vectorの場合は、”イテレータ”というものが使えます。無理矢理日本語にすると”反復子”あるいは”繰り返し子”ですかね。おおよそvectorの各要素へのポインタだと思って間違いはないです。これを使うと、

    for( auto it = people.begin() ; it != people.end() ; it++ )
        std::cout << it->id << " " << it->name << std::endl;

型の推定を使いました。のでauto itでイテレータを宣言しています。先出ししてしまいましたが、実はpeople.begin()とかpeople.end()はイテレータを返していました。ついでにも一つ、範囲for文を使うと、もう少し簡潔に書けます。

for (データ型 変数名 : コンテナ)
{
    // 処理
}

が基本なので、上の例に当てはめると、

$ cat main-ranged-for.cpp
#include <vector>
#include <algorithm>
#include <string>
#include <iostream>

struct Person {
    int id;
    std::string name;
};
int main() {
    std::vector<Person> people = {{2,"Bob"}, {1,"Alice"}, {3,"Charlie"}};
    std::sort(people.begin(), people.end(), [](const Person &a, const Person &b){
        return a.id < b.id;
    });
    for( const auto& it : people )
        std::cout << it.id << " " << it.name << std::endl;
}

jakeb@Orbit-11 MINGW64 ~/Dropbox/vector-sort
$ bcc32c main-ranged-for.cpp
Embarcadero C++ 7.70 for Win32 Copyright (c) 2012-2024 Embarcadero Technologies, Inc.
main-ranged-for.cpp:
Turbo Incremental Link 6.99 Copyright (c) 1997-2024 Embarcadero Technologies, Inc.

jakeb@Orbit-11 MINGW64 ~/Dropbox/vector-sort
$ ./main-ranged-for
1 Alice
2 Bob
3 Charlie

宣言と初期化で要素を追加するのでなく、1要素が得られたら都度vectorに追加していく具体的な例は、次の記事で述べる予定です。C++にはvector以外にも入れ物(コンテナクラス)として、arrayとかqueueとか種々あります。

コメント