std :: vector :: reset()とstd :: vector :: reserved()


80

この投稿のコメントセクションにはstd::vector::reserve() vs std::vector::resize()。の使用に関するスレッドがあります。

元のコードは次のとおりです。

void MyClass::my_method()
{
    my_member.reserve(n_dim);
    for(int k = 0 ; k < n_dim ; k++ )
         my_member[k] = k ;
}

に要素を書き込むにはvector、正しいことはstd::vector::resize()、ではなく、を呼び出すことだと思いstd::vector::reserve()ます。

実際、VS2010 SP1のデバッグビルドでは、次のテストコードが「クラッシュ」します。

#include <vector>

using namespace std;

int main()
{
    vector<int> v;
    v.reserve(10);
    v[5] = 2;

    return 0;
}

私は正しいですか、それとも間違っていますか?そして、VS2010 SP1は正しいですか、それとも間違っていますか?


11
説明は「私は間違っていた」と同じくらい簡単かもしれません:D
Luchian Grigore 2012年

7
@LuchianGrigoreが間違っていることはめったにないので、これを「ローカライズされすぎている」とフラグを付けました
デフォルト

1
@Defaultは、「めったに間違っていない」を「間違いをすばやく修正する」と読みます:)
Luchian Grigore

1
元の投稿のコードが正しく使用されるように更新されresize()、疑問が解消されました。モデレーターの方へ:この質問が「ローカライズされすぎている」場合は削除してください。将来誰かに役立つと思われる場合は、そのままにしておいてください。
Mr.C64 2012年

1
この質問は、プロジェクトをvc6からvs2013に移行するときに、実際に私の疑問を解消します。ありがとう:))
pengMiao19年

回答:


114

理由には2つの異なる方法があります。

std::vector::reserve メモリを割り当てますが、以前と同じ論理サイズを持つベクトルのサイズは変更されません。

std::vector::resize実際にベクトルのサイズを変更し、デフォルト状態のオブジェクトでスペースを埋めます。それらがintの場合、それらはすべてゼロになります。

予約後、あなたの場合、要素5に書き込むために多くのpush_backsが必要になります。それを望まない場合は、サイズ変更を使用する必要があります。

予約に関する1つのこと:次にpush_backを使用して要素を追加すると、予約した容量に達するまで、ベクター内のデータへの既存の参照、イテレーター、またはポインターは引き続き有効です。したがって、1000を予約し、サイズが5の場合&vec[4]、ベクトルに1000個の要素が含まれるまでは同じままになります。その後、呼び出すpush_back()ことができ、動作しますが、&vec[4]以前に保存されたポインタが無効になる可能性があります。


したがって、空のベクトル、つまりvecの場合、予約後vec [1]はセグメント違反で終了します。
hailinzeng 2015年

2
vec [1]は未定義の動作になります。
CashCow 2015年

ウィルstd::vector::reserve上の完全な配列の臨時のコピーを防ぎますかpush_back
ポストセルフ

これはC ++ 11または特定のstd実装専用ですか?予約と[]によるアクセスのあるコードは正常に機能しているように見えますか?godbolt.org/z/MhgFdZ
Steve_Corrin

1
@Steve_Corrin未定義の動作は未定義です。それはうまくいくように見えるかもしれません。それはまだ無効なコードです。< size()コンテナ内に存在しない要素でのインデックス作成は許可されていません。言語の定義によれば、それらはそこには存在しません。コンパイラが核兵器を起動せず、代わりにRAMを思い通りに突く/覗くだけの場合、それは幸運です。または不運だと思います。理想的には、すべてのプログラマーが行うすべての無効なことをキャッチできますが、幸運を祈ります。
underscore_d

25

Jan Hudecがここに回答:vector :: resetize()とvector :: reserved()のどちらかを選択

2つの関数は大きく異なることを行います。

resize()メソッド(およびコンストラクターに引数を渡すことはそれと同等です)は、指定された数の要素をベクトルに挿入します(値を指定するためのオプションの2番目の引数があります)。それはsize()に影響し、反復はそれらすべての要素を調べ、push_backはそれらの後に挿入し、operator []を使用してそれらに直接アクセスできます。

reserved()メソッドはメモリを割り当てるだけで、初期化されないままにします。これはcapacity()にのみ影響しますが、size()は変更されません。ベクトルに何も追加されないため、オブジェクトの値はありません。次に要素を挿入すると、事前に行われているため再割り当ては発生しませんが、それが唯一の効果です。

だからそれはあなたが望むものに依存します。1000個のデフォルト項目の配列が必要な場合は、resize()を使用します。1000個のアイテムを挿入する予定の配列が必要で、いくつかの割り当てを回避したい場合は、reserve()を使用します。

編集:高炉のコメントにより、私はもう一度質問を読み、あなたの場合、正しい答えは手動で事前に割り当ててはいけないことに気づきました。必要に応じて、最後に要素を挿入し続けます。ベクターは必要に応じて自動的に再割り当てされ、前述の手動の方法よりも効率的に再割り当てされます。reserved()が理にかなっている唯一のケースは、事前に簡単に利用できる必要がある合計サイズの合理的に正確な見積もりがある場合です。

EDIT2:広告の質問の編集:最初の見積もりがある場合は、reserve()よりもその見積もりが不十分であることが判明した場合は、ベクターにそれを実行させます。


12

それはあなたが何をしたいかによります。 ;に要素を追加reserveしませvector。を変更するだけcapacity()要素を追加しても再割り当てされない(たとえば、イテレータが無効になる)ことが保証されます。 resizeすぐに要素を追加します。後で要素を追加する場合(insert()push_back())、を使用しますreserve。後で([]またはat()を使用して)要素にアクセスする場合は、を使用しますresize。したがって、youreMyClass::my_methodは次のいずれかになります。

void MyClass::my_method()
{
    my_member.clear();
    my_member.reserve( n_dim );
    for ( int k = 0; k < n_dim; ++ k ) {
        my_member.push_back( k );
    }
}

または

void MyClass::my_method()
{
    my_member.resize( n_dim );
    for ( int k = 0; k < n_dim; ++ k ) {
        my_member[k] = k;
    }
}

どちらを選んだかは好みの問題ですが、引用したコードは明らかに間違っています。


3

ベクトルの現在のサイズよりも小さい数で両方のメソッドが呼び出される場合については、おそらく議論が必要です。

reserve()容量よりも小さい番号でかけても、サイズや容量には影響しません。

resize()現在のサイズよりも小さい番号で呼び出すと、コンテナはそのサイズに縮小され、余分な要素が効果的に破棄されます。

合計するresize()とメモリが解放されますが、解放されreserve()ません。


サイズ変更によってメモリが解放されることはありません。サイズが小さくなると、デストラクタが呼び出されますが、メモリは保持されます(容量は変更されません)。
ジョンゴードン

2

はい、あなたは正しいです、ルチアンはタイプミスをしたばかりで、おそらくコーヒーが不足しすぎて彼の間違いに気づきません。


1

サイズ変更は実際にはベクター内の要素の量を変更します。サイズ変更によってベクターが大きくなると、新しいアイテムがデフォルトで作成されます。

vector<int> v;
v.resize(10);
auto size = v.size();

この場合、サイズは10です。

一方、reserveは、内部バッファを指定されたサイズに拡張することのみを要求し、配列の「サイズ」は変更せず、バッファサイズのみを変更します。

vector<int> v;
v.reserve(10);
auto size = v.size();

この場合、サイズはまだ0です。

したがって、質問に答えるには、そうです。十分なスペースを予約していても、インデックス演算子を使用して初期化されていないメモリにアクセスしています。intを使用すると、それほど悪くはありませんが、クラスのベクトルの場合は、構築されていないオブジェクトにアクセスすることになります。

デバッグモードに設定されたコンパイラの境界チェックは、この動作によって明らかに混乱する可能性があります。これが、クラッシュが発生している理由である可能性があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.