web-dev-qa-db-ja.com

std :: vector :: resize()vs. std :: vector :: reserve()

この投稿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は正しいですか、それとも間違っていますか?

66
Mr.C64

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

std::vector::reserveはメモリを割り当てますが、ベクターのサイズは変更しません。ベクターの論理サイズは以前と同じです。

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

予約した後、あなたの場合、要素5に書き込むために多くのPush_backsが必要になります。そうしない場合は、あなたの場合はresizeを使用する必要があります。

97
CashCow

ここでJan Hudecと答えました: vector :: resize()とvector :: reserve()の間の選択

2つの機能は、大きく異なることを行います。

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

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

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

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

EDIT2:広告の質問の編集:もしあなたが初期の見積もりを持っているなら、その見積もりをreserve()よりも、それが十分でないことが判明したなら、ただベクターにそれをさせてください。

22
lucasg

何をしたいかによって異なります。 reservenotに要素を追加しますvector; capacity()を変更するだけです。これにより、adding要素が再割り当てされない(およびイテレータを無効にするなど)ことが保証されます。 resizeはすぐに要素を追加します。後で要素を追加する場合(insert()Push_back())、reserveを使用します。後で要素にアクセスする場合(_[]_またはat()を使用)、resizeを使用します。したがって、_MyClass::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;
    }
}
_

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

11
James Kanze

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

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

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

要約すると、resize()はメモリを解放しますが、reserve()は解放しません。

2
Dula

はい、あなたは正しいです、Luchianはちょうどタイプミスをしただけで、おそらく彼の間違いに気付くにはコーヒーを奪われすぎています。

2
Konrad Rudolph

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

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

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

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

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

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

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

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

1
odinthenerd