web-dev-qa-db-ja.com

C ++のベクターの初期容量

デフォルトのコンストラクタを使用して作成される_std::vector_のcapacity()とは何ですか? size()がゼロであることを知っています。デフォルトで構築されたベクターはヒープメモリ割り当てを呼び出さないと言えますか?

この方法では、std::vector<int> iv; iv.reserve(2345);のような単一の割り当てを使用して、任意の予約で配列を作成することができます。何らかの理由で、2345でsize()を開始したくないとしましょう。

たとえば、Linux(g ++ 4.4.5、カーネル2.6.32 AMD64)の場合

_#include <iostream>
#include <vector>

int main()
{
  using namespace std;
  cout << vector<int>().capacity() << "," << vector<int>(10).capacity() << endl;
  return 0;
}
_

印刷_0,10_。それはルールですか、それともSTLベンダーに依存していますか?

73
Notinlist

標準では、コンテナの最初のcapacityが何であるかが指定されていないため、実装に依存しています。一般的な実装では容量がゼロから始まりますが、保証はありません。一方、std::vector<int> iv; iv.reserve(2345);の戦略を改善する方法はありません。

59
Mark Ransom

Std :: vectorのストレージ実装は大きく異なりますが、私が遭遇したものはすべて0から始まります。

次のコード:

#include <iostream>
#include <vector>

int main()
{
  using namespace std;

  vector<int> normal;
  cout << normal.capacity() << endl;

  for (unsigned int loop = 0; loop != 10; ++loop)
  {
      normal.Push_back(1);
      cout << normal.capacity() << endl;
  }

  std::cin.get();
  return 0;
}

次の出力を提供します。

0
1
2
4
4
8
8
8
8
16
16

gCC 5.1および次の場合:

0
1
2
3
4
6
6
9
9
9
13

mSVC 2013で。

22
metamorphosis

標準を理解している限り(実際には参照に名前を付けることはできませんでしたが)、コンテナのインスタンス化とメモリ割り当ては、正当な理由で意図的に切り離されています。そのため、個別の個別の呼び出しがあります

  • constructorはコンテナ自体を作成します
  • reserve()は、適切な大きさのメモリブロックを事前に割り当てて、指定された数のオブジェクトを少なくとも(!)収容します。

そして、これは非常に理にかなっています。 reserve()に存在する唯一の権利は、ベクトルを成長させるときに、おそらく高価な再割り当てをコード化する機会を与えることです。有用であるためには、保存するオブジェクトの数を知っているか、少なくとも経験に基づいた推測ができる必要があります。これが与えられていない場合は、無駄なメモリの再割り当てを変更するだけなので、reserve()に近づかないほうがよいでしょう。

したがって、すべてをまとめると:

  • 標準は意図的にnot特定の数のオブジェクトにメモリブロックを事前に割り当てることができるコンストラクターを指定します(これは実装固有の固定された「何か」を内部で割り当てるよりも少なくとも望ましいでしょう)。
  • 割り当ては暗黙的であってはなりません。したがって、ブロックを事前に割り当てるには、reserve()を個別に呼び出す必要があり、これは同じ構築場所である必要はありません宿泊)
  • したがって、ベクトルが常に実装定義のサイズのメモリブロックを事前に割り当てる場合、これはreserve()の意図されたジョブを妨げるでしょうか?
  • STLがベクトルの意図された目的と予想されるサイズを自然に知ることができない場合、ブロックを事前に割り当てることの利点は何でしょうか?逆効果ではないとしても、かなり無意味です。
  • 適切な解決策は、代わりに最初のPush_back()で特定のブロックを割り当てて実装することです-reserve()によって以前に明示的に割り当てられていない場合。
  • 必要な再割り当ての場合、ブロックサイズの増加も実装固有です。私が知っているベクターの実装は、サイズの指数関数的な増加から始まりますが、大量のメモリを浪費したり、さらには消費したりすることを避けるために、一定の最大値で増分率を制限します。

これはすべて、割り当てコンストラクタによって妨げられない場合にのみ、完全な操作と利点になります。 reserve()(およびshrink_to_fit())によって要求に応じてオーバーライドできる一般的なシナリオの合理的なデフォルトがあります。そのため、標準が明示的にそう述べていない場合でも、新しく構築されたベクトルが事前に割り当てられないことは、現在のすべての実装にとって非常に安全な賭けであると確信しています。

6
Don Pedro

他の回答へのわずかな追加として、Visual Studioでデバッグ条件下で実行すると、容量がゼロから始まっても、デフォルトで構築されたベクターがヒープに割り当てられることがわかりました。

具体的には、_ITERATOR_DEBUG_LEVEL!= 0の場合、vectorはイテレーターのチェックに役立つスペースを割り当てます。

https://docs.Microsoft.com/en-gb/cpp/standard-library/iterator-debug-level

当時はカスタムアロケーターを使用していましたが、余分な割り当てを期待していなかったため、これは少し面倒です。

4
David Woo

標準では容量の初期値は指定されていませんが、最大サイズを超えない限り、STLコンテナーは自動的に大きくなり、入力したデータに対応します(max_sizeメンバー関数を使用してください)。ベクトルと文字列の場合、より多くのスペースが必要になるたびにreallocによって成長が処理されます。値1〜1000を保持するベクトルを作成するとします。予約を使用しない場合、コードは通常、次のループ中に2〜18の再割り当てになります。

vector<int> v;
for ( int i = 1; i <= 1000; i++) v.Push_back(i);

予約を使用するようにコードを変更すると、ループ中に割り当てが0になる場合があります。

vector<int> v;
v.reserve(1000);

for ( int i = 1; i <= 1000; i++) v.Push_back(i);

おおまかに言うと、ベクトルと文字列の容量は、毎回1.5〜2倍に増加します。

2
Archie Yalakki