HOWTO · C++

C++ のベクトルへのポインタからメンバ関数にアクセスする

C++ でポインタ経由で std::vector のメンバを呼び出す方法、ベクトルへのポインタを関数に渡す方法、その要素を反復処理する方法を学びます。

このページの内容

C++でポインタを通じてベクトルのメンバーを呼び出すには、ptr->member を使います。同じ操作は (*ptr).member とも書けます。ポインタが指す先は、ベクトルの要素ではなくベクトルオブジェクト自体でなければなりません。オブジェクトのアドレスを & で取得し、size() や at() などのメンバーにどちらかの式でアクセスします。次の例では、ポインタを関数に渡し、そこから各値を読み取ります。

-> を使ってポインタ経由でベクトルにアクセスする

ポインタは、ベクトル型の後ろに * を付けて宣言します。整数のベクトルなら、型は std::vector<int>* です。既存のベクトル values のアドレスは &values です。これをポインタに代入すると、そのポインタはベクトルを指します。ベクトルがコピーされたり、別のコレクションが作られたりするわけではありません。

アロー演算子は、ポインタの間接参照とメンバーの選択をまとめて行います。そのため、ptr->size() は ptr が指すベクトルの size() を呼び出します。同じポインタは、一致するポインタ型の引数を受け取る関数にも渡せます。完全な例では、ベクトルを読み取るだけなので、report_size は const ベクトルへのポインタを受け取ります。const 修飾により、その関数は引数を通じてベクトルを変更できません。ポインタの渡し方は変わりません。

次の例を実行します。

#include <cstddef>
#include <iostream>
#include <vector>

void report_size(const std::vector<int>* ptr) {
    std::cout << "Count: " << ptr->size() << '\n';
}

int main() {
    std::vector<int> values{10, 20, 30};
    const std::vector<int>* ptr = &values;

    report_size(ptr);
    for (std::size_t i = 0; i < ptr->size(); ++i) {
        std::cout << (*ptr).at(i) << '\n';
    }
}
Count: 3
10
20
30

実行すると、ベクトルのサイズに続いて、各値が1行ずつ表示されます。ベクトルは自動記憶域期間を持つローカル変数であり、ポインタはそのベクトルがスコープ内にある間だけ使われます。自分のプログラムでも、ポインタがアドレスを保持しているオブジェクトより長く使われないようにしてください。

(*ptr).member を使ってポインタ経由でベクトルにアクセスする

式 (*ptr).member は、まず ptr を間接参照してベクトルオブジェクトを得てから、. でそのメンバーを選択します。括弧は重要です。括弧がないと、間接参照より先にドット演算子が ptr に結び付き、意図した操作になりません。ベクトルのメンバーに対しては、(*ptr).size() と ptr->size() は同じ意味です。アロー形式は短く、間接参照してからドットで選択する形式は、2つの操作を明示します。

上の例では、(*ptr).at(i) はベクトルへのポインタを間接参照した後、インデックスで要素にアクセスします。メンバー呼び出しはベクトルに対して行われ、at(i) が選択した整数を返します。この違いを理解しておくと、コードを読むときに役立ちます。ptr の型はベクトルへのポインタですが、(*ptr).at(i) が表すのは要素の値です。関数がベクトルを扱うならベクトルへのポインタを渡し、1つの要素を扱うなら要素への参照、または適切な要素ポインタを使います。

ベクトルへのポインタを通じて要素を反復処理する

このループでは、終了条件に ptr->size() を使い、各値には (*ptr).at(i) でアクセスします。どちらの操作もベクトルへのポインタを通じて行われます。インデックスによるメンバー呼び出しは、アロー形式の ptr->at(i) と書くこともでき、同じベクトルメンバーを選択します。<cstddef> から読み込む std::size_t は符号なし型で、ベクトルのサイズやインデックスに適しています。

ベクトルへのポインタは、ベクトルのイテレーターとも、個々の要素へのポインタとも異なります。この例のポインタは、std::vector<int> オブジェクト全体を指します。イテレーターは要素をたどるためにコンテナーが提供する位置を表し、要素ポインタは格納された値の1つを指します。インデックス付きループではこれらの役割が混同されず、インデックスが必要な場合に便利です。すべての値を順に処理するだけなら、*ptr を使った範囲ベースのループも可能です。ただし、そのループはベクトルを反復するため、各ステップでポインタを通じてメンバーにアクセスする例にはなりません。

例では反復中にベクトルを変更しないため、インデックスによるアクセスをそのまま使えます。挿入や削除をしながら走査するプログラムでは、ループを見直してください。こうした操作はベクトルのサイズを変え、要素への参照、ポインタ、イテレーターを無効にすることがあります。ベクトルオブジェクトへのポインタは引き続きそのオブジェクトを指しますが、要素の位置が安定するわけではありません。ポインタを使う間はベクトルを生存させ、プログラム内での変更を考慮した反復方法を選んでください。