この節では、vector データ型で提供される各メンバー関数の詳細を説明します。これらのメンバー関数は vector の基本演算を提供します。これらは、第 13 章で説明する汎用アルゴリズムで大幅に拡張することができます。
これはテンプレートクラスであるため、vector の宣言に構成要素の型の指示を取り込む必要があります。integer や double、ポインタ型、ユーザーが定義した型などと同様に、基本言語型とすることもできます。ユーザーが定義した型の場合、このコンストラクタは新しく作成された要素の初期化に使用されるため、ユーザーが定義した型がコピーコンストラクタを実装する必要があります。
重要: vector で保持される要素は、コピーコンストラクタを定義する必要があります。vector クラスの関数では使用されませんが、一部の汎用アルゴリズムも、等価演算子 == または関係小なり演算子 < のいずれかを認識するための vector 要素を要求します。
array と同様に、vector は一般的に、vector が保持する要素の数や各要素の初期値を記述する整数の引数で宣言されます。
vector<int> vec_one(10,0);
他の標準ライブラリのコンテナと同様に、vector の場合も、アロケータ型が追加のテンプレートパラメータとして提供されます。
vector<int,allocator<int>>
この 2 つの宣言は同じ意味です。アロケータのテンプレートパラメータが明示的に提供される場合、その型が <> に囲まれている型と一致する必要があります。
この場合のように、型にデフォルトコンストラクタがある場合は、2 番目の引数を省略することができます。ここで vector の作成に使用されるコンストラクタは explicit として宣言され、変換演算子として使用されなくなります。状況によっては、意図せずに integer が vector に変換される可能性があるため、これは一般的に有効な方法です。
vector の作成に使用できる、さまざまな形式の多数のコンストラクタがあります。サイズが指定されない場合、最初は vector に要素がなく、要素が追加されると、自動的にサイズが大きくなります。コピーコンストラクタは、他の vector から vector のクローンを作成します。
vector<int> vec_two(5, 3); // コピーコンストラクタ vector<int> vec_three; vector<int> vec_four(vec_two); // 代入による初期化
また、vector は他のコレクションからの要素を使用して、開始と終了の反復子の組み合わせによって初期化することができます。コレクションは、サポートする標準 C++ ライブラリの任意のコンテナクラスから引き出された値で反復子を初期化できるため、反復子の引数はどのような形式でも構いません。
vector <int> vec_five (aList.begin(), aList.end());
注: クラステンプレートと異なるテンプレート引数でメソッドを定義する機能が必要とされるため、一部のコンパイラは反復子を使用するコンテナの初期化をサポートしていません。コンパイラの技術は標準 C++ ライブラリの定義に適合しつつあり、Rogue Wave の標準 C++ ライブラリのバージョンはこの方式の慣用ポインタと vector 反復子をサポートしています。
vector に他の vector の値を代入することができます。この場合、ターゲットは引数 vector のコピーを受け取ります。
vec_three = vec_five;
assign() メンバー関数は代入と似ていますが、より汎用性があり、より多くの引数を要求する場合もあります。代入と同じく、コンテナの既存の値が削除され、引数で指定された値で置換されます。assign() には 2 つの形式があります。1 つは、既存のコンテナのサブシーケンスを指定する 2 つの反復子引数をとります。このサブシーケンスからの値は、受け手の新しい要素となります。もう 1 つの assign() は、数とコンテナ要素型の任意の値をとります。呼び出されると、コンテナは、コンテナ型のデフォルト値または指定された初期値のいずれかに等しい数で指定された数だけの要素を保持します。
vec_six.assign(list_ten.begin(), list_ten.end()); vec_four.assign(3, 7); // 値 7 の 3 個のコピー vec_five.assign(12); // 値 0 の 12 個のコピー
デストラクタがコンテナの要素型に定義されている場合、デストラクタが呼び出されると、コレクションから各値が削除されます。
最後に、2 つの vector は swap() 演算によって内容全体を交換することができます。引数コンテナが受け手の値を引き受ける一方、受け手はそれらが引数であるとみなします。交換は非常に効率がよく、適切な場合には、要素対要素の明示的な転送に優先して使用するべきです。
vec_three.swap(vec_four);
クラス vector には多数の型の定義が含まれ、一般的にそのほとんどが宣言文に使用されます。たとえば、integer の vector のための反復子は、次の形式で宣言することができます。
vector<int>::iterator location;
iteratorに加えて、表 8 では次の型を定義します。
表 8 -- クラス vector の型の定義
型 | 定義 |
---|---|
value_type |
vector が維持する要素に関連付けられた型 |
const_iterator |
配下のシーケンスの変更を許可しない反復子 |
reverse_iterator |
逆方向に移動する反復子 |
const_reverse_iterator |
定数反復子と逆方向反復子の組み合わせ |
reference |
配下要素への参照 |
const_reference |
要素が変更されることを許可しない配下要素への参照 |
size_type |
符号なし整数型、コンテナのサイズを示すために使用される |
difference_type |
符号付き整数型、反復子間の距離の記述に使用される |
allocator_type |
vector のメモリー管理に使用されるアロケータの型 |
特定のインデックスで vector によって維持される値は、通常の配列と同様に、添字演算子を使用してアクセスまたは変更をすることができます。また、配列と同様に、インデックス値の妥当性を検証する機能はありません。定数 vector のインデックス付けは定数参照を算出します。正当な値の範囲外で vector のインデックス付けを試みると、予測不能の擬似結果が生成されます。
cout << vec_five[1] << endl; vec_five[1] = 17;
メンバー関数 at() は、添字演算子の代わりに使用することができます。これは添字演算子とまったく同じ引数を使用し、まったく同じ値を返しますが、引数が無効の場合は、範囲外の例外を送出します。
メンバー関数 front() は vector の最初の要素を返し、一方、メンバー関数 back() は最後の要素を算出します。また、定数 vector に適用されると、両方が定数参照を返します。
cout << vec_five.front() << " ... " << vec_five.back() << endl;
一般的に、vector に関連付けられている サイズ は 3 種類です。
この 3 つの値は、以下のメンバー関数 size()、 capacity()、max_size() によってそれぞれ算出されます。
cout << "size: " << vec_five.size() << endl; cout << "capacity: " << vec_five.capacity() << endl; cout << "max_size: " << vec_five.max_size() << endl;
最大サイズは、通常、利用可能なメモリーの量、またはデータ型 size_type によって記述できる最大値でのみ制限されます。現在のサイズと容量の特性を記述するのは、より困難です。5.2.5 節で記述するように、要素はさまざまな方法で vector へ追加したり、削除したりすることができます。vector から要素が削除されると、通常、vector のメモリーが再び割り当てられることはないためサイズは減少しますが、容量は変化しません。元の容量を超えない場合、後続の挿入によって新しいメモリーの再割り当てが強制されることはありません。
挿入によってサイズが容量を超えると、通常、vector 要素を保持するために新しいメモリーブロックが割り当てられます。要素の型に適した代入演算子を使用して、この新しいメモリーに値がコピーされ、古いメモリーは削除されます。これは潜在的にメモリーを大量に消費する演算であるため、vector データ型は、プログラマが vector の容量を指定する手段を提供します。メンバー関数 reserve() は vector への指示であり、vector が少なくとも既定のサイズになるように期待されていることを示します。reserve() で使用される引数が現在の容量よりも大きい場合、再割り当てが発生し、引数値が新しい容量となります (その後、さらに大きくなる場合があります。引数として指定された値が限界となる必要はなく、推測値で構いません)。容量がすでに引数を超えている場合、再割り当ては行われません。reserve() が呼び出されても vector のサイズも要素の値自体も変更されませんが、例外として潜在的に移動される可能性があり、この場合は再割り当てが行われます。
vec_five.reserve(20);
再割り当ては、vector に保持される要素を参照するすべての参照、ポインタ、反復子を無効にします。
vector の現在のサイズが 0 の場合、vector の容量に関係なく、メンバー関数 empty() が真を返します。この関数の使用は、通常、size() によって返される結果が 0 になる場合と比較すると、より効果的です。
cout << "empty is " << vec_five.empty() << endl;
メンバー関数 resize() は、引数で指定された値に対する vector のサイズを変更します。値は、必要に応じて、コレクションの末尾へ追加されたり、消去されます。任意の 2 つ目の引数は、コレクションへ追加される新しい要素の初期値を提供するために使用することができます。デフォルトコンストラクタが型に含まれない場合、この引数では任意ではありません。デストラクタが要素の型に定義されている場合は、コレクションから削除された値のためにデストラクタが呼び出されます。
メンバー関数 resize() は、引数で指定された値に対する vector のサイズを変更します。値は、必要に応じて、コレクションの末尾へ追加されたり、消去されます。任意の 2 つ目の引数は、コレクションへ追加される新しい要素の初期値を提供するために使用できます。デフォルトコンストラクタが型に含まれない場合、この引数では任意ではありません。デストラクタが要素の型に定義されている場合は、コレクションから削除された値のためにデストラクタが呼び出されます。
// 必要に応じて、値が 17 追加され、サイズが 12 になる vec_five.resize (12, 17);
注: vector は、1 つの大きなメモリーのブロックに値を格納します。一方、deque は多数の小さなブロックを使用します。1 つのメモリーのブロックのサイズを制限するマシン上では、deque が vector よりも大きなコレクションを保持できるため、この相違は重要です。
前述のように、クラス vector は通常の配列とは異なり、特定の状況では vector のサイズを大きくしたり、小さくしたりすることが可能です。挿入によって vector に保持される要素の数が値の保持に使用されるメモリーブロックの現在の容量を超えると、新しいブロックが割り当てられ、要素が新しい記憶領域にコピーされます。
注: 1 つの要素を vector に追加しただけでも、最悪の場合は、各要素が新しい位置に移動されると、vector の要素数が時間に比例することが要求されます。挿入が最も顕著な問題である場合、挿入演算に最適化されている list や set などのコンテナを使用する可能性を調べる必要があります。
関数 push_back() を使用して vector の末尾に新しい要素を追加することができます。現在の割り当てに空きがある場合、この演算は非常に効果的です (一定時間)。
vec_five.push_back(21); // コレクションの終端に要素 21 を追加する
対応する削除演算は pop_back() であり、vector のサイズを小さくしますが、容量は変更しません。コンテナの型がデストラクタを定義する場合、デストラクタが削除される値に呼び出されます。ここでも、この演算が非常に効果的です。第 7 章で説明するように、クラス deque はコレクションの末尾と先頭の両方へ値を追加したり、削除することを許可します。
より一般的な挿入演算は、insert() メンバー関数を使用して実行することができます。挿入の位置は反復子によって記述され、 示された位置の直前に挿入されます。定数要素の固定された数は、1 つの関数呼び出しによって挿入することができます。要素のブロックを挿入すると、1 つの呼び出しで一度に割り当てが実行されるため、個々の挿入のシーケンスを実行するよりもはるかに効率的です。
より一般的な挿入演算は、insert() メンバー関数を使用して実行できます。挿入の位置は反復子によって記述され、 示された位置の直前に挿入されます。定数要素の固定された数は、1 つの関数呼び出しによって挿入できます。要素のブロックを挿入すると、1 つの呼び出しで一度に割り当てが実行されるため、個々の挿入のシーケンスを実行するよりもはるかに効率的です。
// 7 の位置を検出する vector<int>::iterator where = find(vec_five.begin(), vec_five.end(), 7); // 7 の前に 12 を挿入する vec_five.insert(where, 12); vec_five.insert(where, 6, 14); // 14 の 6 つのコピーを挿入する
insert() メンバー関数の最も一般的な形式は、他方のコンテナからのサブシーケンスを示す、1 つの位置と 1 組の反復子です。シーケンスで記述される値の範囲は、vector に挿入されます。ここでも、この関数を使用すると、一度に割り当てが実行されるため、個々の挿入のシーケンスを使用するよりも効率的です。
vec_five.insert (where, vec_three.begin(), vec_three.end());
注: 挿入による再割り当てが行われると、削除されたメモリーブロック内の位置を示すすべての参照、ポインタ、反復子が無効になることに注意してください。
vector の終端から要素を削除する pop_back() メンバー関数に加えて、反復子を使用して位置を示し、vector の中間から要素を削除する関数もあります。このタスクを実行するメンバー関数は erase() であり、以下の 2 つの形式があります。
1 つの反復子を使用し、個々の値を削除する形式
1 組の反復子を使用し、指定された範囲内のすべての値を削除する形式。vector のサイズは減少しますが、 容量は変化しません。コンテナの型がデストラクタを定義する場合、削除される値にデストラクタが呼び出されます。
vec_five.erase(where); // 終端から 12 を消去 where = find(vec_five.begin(), vec_five.end(), 12); vec_five.erase(where, vec_five.end());
メンバー関数 begin() と end() は、コンテナのランダムアクセス反復子を算出します。これらの演算によって算出される反復子は、要素の挿入または削除の後で無効となる場合があることに注意してください。メンバー関数 rbegin() と rend() も同様の反復子を返しますが、これらは逆の順序で配下要素にアクセスします。元のコンテナが定数として宣言されている場合や、代入やパラメータのターゲットが定数の場合は、定数反復子が返されます。
vector は、特定の値がコレクションに含まれているかどうかの判定に使用できるメソッドを直接的には提供しません。しかし、汎用アルゴリズム find() または count() はこの目的のために使用することができます (13.3.1 節 と 13.6.1 節を参照)。たとえば、次の文は整数 vector に要素 17 が含まれるかどうかをテストします。
count (vec_five.begin(), vec_five.end(), 17);
コンパイラが部分的な特殊化をサポートしない場合は、代わりに次のインタフェースを使用する必要があります。
int num = 0; count (vec_five.begin(), vec_five.end(), 17, num); if (num) cout << "contains a 17" << endl; else cout << "does not contain a 17" << endl;
注: count() は、参照によって渡される引数による結果を返します。この関数を呼び出す前に、この値が適切に初期化されていることが重要です。
vector が自動的に値のシーケンスを維持することはありません。しかし、汎用アルゴリズム sort() を使用して vector を順序付けることは可能です (14.2 節を参照)。ソートの最も単純な形式は 要素の型の小なり演算子による比較に使用されます。別の汎用アルゴリズムを使用すると、プログラマが比較演算子を明示的に指定することができます。これは、たとえば、昇順ではなく降順で要素を配置するために使用することができます。
vector が自動的に値のシーケンスを維持することはありません。しかし、汎用アルゴリズム sort() を使用して vector を順序付けることは可能です (14.2 節を参照)。ソートの最も単純な形式は 要素の型の小なり演算子による比較に使用されます。別の汎用アルゴリズムを使用すると、プログラマが比較演算子を明示的に指定できます。これは、たとえば、昇順ではなく降順で要素を配置するために使用できます。
// 昇順にソートする sort (aVec.begin(), aVec.end()); // 順序付け関数を明示的に指定して、降順にソートする sort (aVec.begin(), aVec.end(), greater<int>() ); // 降順にソートするための別の方法 sort (aVec.rbegin(), aVec.rend());
第 14 章で説明する多数の演算を、順番付けられたコレクションを保持する vector に適用することができます。たとえば、汎用アルゴリズム merge() を使用することで、2 つの vector をマージすることができます (14.6 節を参照)。
第 14 章で説明する多数の演算を、順番付けられたコレクションを保持する vector に適用できます。たとえば、汎用アルゴリズム merge() を使用すると、2 つの vector をマージできます (14.6 節を参照)。
// 2 つの vector をマージし、出力を印刷する merge (vecOne.begin(), vecOne.end(), vecTwo.begin(), vecTwo.end(), ostream_iterator<int,char> (cout, " "));
また、vector のソートにより、find() などの線形トラバーサルアルゴリズムの代わりに、より効率のよい 2 等分探索アルゴリズムを実行できるようになります (14.5 節を参照)
第 IV 部で説明するアルゴリズムのほとんどは vector で使用することができますが、中には他よりも有益なものがあります。たとえば、vector の最大値は、次のように定義することができます。
第 4 部で説明するアルゴリズムのほとんどは vector で使用できますが、中には他よりも有益なものがあります。たとえば、vector の最大値は、次のように定義できます。
vector<int>::iterator where = max_element (vec_five.begin(), vec_five.end()); cout << "maximum is " << *where << endl;
表 9 は、vector で特に有益なアルゴリズムの一覧です。
表 9 -- vector で有益な汎用アルゴリズム
アルゴリズム | 目的 |
---|---|
fill |
vector に既定の初期値を割り当てる |
copy |
1 つのシーケンスを別のシーケンスにコピーする |
generate |
ジェネレータから値を vector にコピーする |
find |
条件に一致する要素を検索する |
adjacent_find |
連続する複製要素を検索する |
search |
vector 内のサブシーケンスを検索する |
max_element, min_element |
最大要素または最小要素を検出する |
reverse |
要素の順序を反転する |
replace |
要素を新しい値に置換する |
rotate |
中心点を軸に要素を回転する |
partition |
要素を 2 つのグループに区分する |
next_permutation |
順列を生成する |
inplace_merge |
vector 内でマージする |
random_shuffle |
vector 内で要素をランダムにシャフルする |
count |
条件を満たす要素の数を数える |
accumulate |
vector を減らして 1 つの値にする |
inner_product |
2 つの vector の内積 |
equal |
2 つの vector の等価性をテストする |
lexicographical_compare |
字句比較 |
transform |
vector に変換を適用する |
partial_sum |
値の部分和 |
adjacent_difference |
隣接する値の差 |
for_each |
各要素に対して関数を実行する |