vector :: resize()とvector :: reserve()の間の選択


151

vectorメンバー変数にメモリを事前に割り当てています。以下のコードは最小限の部分です

class A {
  vector<string> t_Names;
public:
  A () : t_Names(1000) {}
};

ある時点で、t_Names.size()がに等しい場合1000。サイズを大きくしていきたいと思います100。次に、それがに達した場合1100、再び増加100します。

私の質問はの間で選択するのか、あるvector::resize()vector::reserve()。この種のシナリオでより良い選択はありますか?

編集:私はある種の正確な見積もりを持っていますt_Names。と推定700800ます。ただし、特定の(まれな)状況では、それよりも大きくなる可能性があり1000ます。


34
これを行うと、ベクトルの成長が一定の時間償却されなくなり、を使用することによるパフォーマンス上の利点の1つが失われることを理解していますstd::vector
高炉2009

1
関連、ドクターズドブスサイトのC ++ Made Easy:Vectors Growをご覧ください。
JWW

回答:


262

2つの関数は大きく異なることをします!

resize()方法(およびコンストラクタへの引数を渡すことと同等である)は、(それがその値を指定するためのオプションの第二引数を有する)サイズ所与作るためにベクターに要素の適切な番号を挿入または削除されます。これはに影響しsize()、反復はこれらすべての要素を調べ、push_backはそれらの後に挿入し、を使用してそれらに直接アクセスできますoperator[]

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

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

編集: Blastfurnaceのコメントにより、質問をもう一度読んで、あなたの場合、正しい答えは手動で事前に割り当てられないことに気づきました。必要に応じて、最後に要素を挿入してください。ベクターは、必要に応じて自動的に再配分し、それを行いますより効率的にマニュアルの方法が述べたよりも。reserve()意味のある唯一のケースは、事前に簡単に利用できる必要がある合計サイズのかなり正確な見積もりがある場合です。

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


質問を編集しました。の見積もりは確かvectorです。
iammilind 2011

3
@Jan:ええと、それはあなたが自分で必要なプロパティを維持するのがどれほど難しいかに応じて壊れやすいかどうかではありません。のようなものx.reserve(x.size() + newdata); vector<int>::iterator special_element = get_special_element(x); for (int i = 0; i < newdata; ++i) { if some_function(i, special_element) x.push_back(i); }は、スペースの予約に関する限り、かなり堅牢です。実際に追加される要素の数はわかりませんが、上限があります。もちろん、疑わしい場合は、イテレータの代わりにインデックスを使用できるベクトルを使用すれば、その違いは通常無視できます。
スティーブジェソップ2011

4
あなたの言い回しは、正しい答えをすでに知っている人には理にかなっていますが、質問をする必要がある人々を簡単に誤解させる可能性があります。「resize()...は、指定された数の要素をベクターに挿入します」-初めて使用する場合にのみ真です-通常、要求された数と既存のの差を挿入しsize()ます。「reserve()メソッドはメモリを割り当てるだけです」- capacity()すでに十分であるかどうかに応じてメモリを割り当てる場合とそうでない場合があり、要素を移動して元のメモリの割り当てを解除する必要がある場合もあります。「いくつかの割り当てを避けたい」とコピーなど
Tony Delroy

18
実際には、プッシュする前に予約することが不可欠であり、使用する必要があります。ある種の3Dモデルローダーをコーディングしていて、モデルに15000個の頂点があると仮定します。最初に事前に割り当てずに、ロード中に各頂点をpush_backしようとすると、非常に時間がかかります。私はそれを個人的に経験し、1万点近くの頂点を持つ自動車の.objモデルをロードしようとしました。30秒かかりました。次に、.reserve()で事前割り当てを使用してコードをリファクタリングしました。今では3秒かかります。コードの先頭に.reserve(100000)を置くだけで、27秒節約できました。
deiz 2013年

1
@denizこれは100,000スケールではささいなことですが、100〜300スケールでは非常に当てはまりません。
deworde 2017年

30

resize()メモリを割り当てるだけでなく、引数として渡す必要なサイズと同じ数のインスタンスを作成します。ただし、メモリを割り当てるだけで、インスタンスは作成されません。あれは、resize()reserve()

std::vector<int> v1;
v1.resize(1000); //allocation + instance creation
cout <<(v1.size() == 1000)<< endl;   //prints 1
cout <<(v1.capacity()==1000)<< endl; //prints 1

std::vector<int> v2;
v2.reserve(1000); //only allocation
cout <<(v2.size() == 1000)<< endl;   //prints 0
cout <<(v2.capacity()==1000)<< endl; //prints 1

出力(オンラインデモ):

1
1
0
1

したがってresize()、デフォルトで作成されたオブジェクトが必要ない場合は、望ましくない場合があります。それも遅くなります。その上、それにpush_back()新しい要素がある場合、size()ベクトルのは、新しいメモリを割り当てることによってさらに増加します(これは、既存の要素を新しく割り当てられたメモリ空間に移動することも意味します)。reserve()最初に、十分なメモリが割り当てられていることを確認するために使用した場合size()、ベクトルにアクセスすると増加しますpush_back()が、予約した領域がなくなるまで新しいメモリは割り当てられません


6
した後reserve(N)operator []無害に使用できます。正しい?
iammilind 2011

2
ほとんどの実装は、あなたがでリクエスト正確な金額を割り当てますがreserve、仕様は、それが、少なくともいくつかの実装は1000よりも高い能力を示したため、多くのいくつかの境界に切り上げるともなるように割り振る必要が
ジャン・ヒューデック

16
@iammilind:いいえ、インデックスが以上の場合v.size()。ベクトルreserve(N)は変化しないことsize()に注意してください。
Nawaz

5
@iammilind:不正解です。reSERVEを呼び出した後、エントリは追加されず、それらを追加するための十分なメモリのみが取得されます。
Jan Hudec、2009

2

説明から、ベクトルt_Namesの割り当てられたストレージスペースを「予約」したいようです。

resize割り当てるreserveだけで構成しない、新しく割り当てられたベクトルを初期化することに注意してください。したがって、「予約」は「サイズ変更」よりはるかに高速です。

サイズ変更予約の違いに関するドキュメントを参照できます


1
代わりにここを参照してください:ベクトル容量 なぜですか?
sehe

1
リンクを追加してくれてありがとう、sehe
ディップ

2

予約時にオブジェクトを初期化したくない場合は、予約してください。また、サイズを変更するときに、その数と使用数を論理的に区別して追跡することもできます。したがって、インターフェイスには動作上の違いがあります。ベクトルは、予約時に同じ数の要素を表し、シナリオでサイズを変更すると100要素大きくなります。

この種のシナリオにはもっと良い選択がありますか?

デフォルトの振る舞いと戦うとき、それはあなたの目的に完全に依存します。カスタマイズされたアロケーターを好む人もいますが、プログラムで何を解決しようとしているのかをよく理解して、アドバイスを得る必要があります。

fwiw、多くのベクトルの実装は、割り当てられた要素の数を増やす必要があるときに単純に2倍にします-ピーク割り当てサイズを最小化しようとしていますか?


予約時にオブジェクトを初期化したくない場合は予約してください。」正しい定式化は、オブジェクトを存在させたくない場合です。これは、オブジェクトを読み取ることはできないが割り当てられる可能性がある、単純に構築可能な型の初期化されていない配列のようなものではありません。むしろ、メモリのみが予約されますが、その中にオブジェクトは存在しないため、operator[]またはを使用してアクセスすることはできません。
underscore_d
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.