C++中向量的初始容量

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12271017/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 16:06:30  来源:igfitidea点击:

Initial capacity of vector in C++

c++memory-managementstlvector

提问by Notinlist

What is the capacity()of an std::vectorwhich is created using the default constuctor? I know that the size()is zero. Can we state that a default constructed vector does not call heap memory allocation?

什么是capacity()std::vector这是使用默认constuctor产生的?我知道size()是零。我们可以声明默认构造的向量不会调用堆内存分配吗?

This way it would be possible to create an array with an arbitrary reserve using a single allocation, like std::vector<int> iv; iv.reserve(2345);. Let's say that for some reason, I do not want to start the size()on 2345.

通过这种方式,可以使用单个分配创建具有任意保留的数组,例如std::vector<int> iv; iv.reserve(2345);. 假设出于某种原因,我不想size()在 2345上启动。

For example, on Linux (g++ 4.4.5, kernel 2.6.32 amd64)

例如,在 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;
}

printed 0,10. Is it a rule, or is it STL vendor dependent?

印刷的0,10。这是规则,还是依赖于 STL 供应商?

采纳答案by Mark Ransom

The standard doesn't specify what the initial capacityof a container should be, so you're relying on the implementation. A common implementation will start the capacity at zero, but there's no guarantee. On the other hand there's no way to better your strategy of std::vector<int> iv; iv.reserve(2345);so stick with it.

该标准没有指定capacity容器的初始值应该是什么,因此您依赖于实现。一个常见的实现将从零开始容量,但不能保证。另一方面,没有办法改善你std::vector<int> iv; iv.reserve(2345);坚持下去的策略。

回答by metamorphosis

Storage implementations of std::vector vary significantly, but all the ones I've come across start from 0.

std::vector 的存储实现差异很大,但我遇到的所有实现都是从 0 开始的。

The following code:

以下代码:

#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;
  }

  cin.get();
  return 0;
}

Gives the following output:

给出以下输出:

0
1
2
4
4
8
8
8
8
16
16

under GCC 5.1 and:

根据 GCC 5.1 和:

0
1
2
3
4
6
6
9
9
9
13

under MSVC 2013.

在 MSVC 2013 下。

回答by Don Pedro

As far as I understood the standard (though I could actually not name a reference), container instanciation and memory allocation have intentionally been decoupled for good reason. Therefor you have distinct, separate calls for

据我了解标准(尽管我实际上无法命名参考),容器实例化和内存分配有充分的理由有意分离。因此,您有不同的、单独的要求

  • constructorto create the container itself
  • reserve()to pre allocate a suitably large memory block to accomodate at least(!) a given number of objects
  • constructor创建容器本身
  • reserve()预先分配一个合适的大内存块来容纳至少(!)给定数量的对象

And this makes a lot of sense. The only right to exist for reserve()is to give you the opportunity to code around possibly expensive reallocations when growing the vector. In order to be useful you have to know the number of objects to store or at least need to be able to make an educated guess. If this is not given you better stay away from reserve()as you will just change reallocation for wasted memory.

这很有意义。唯一存在的权利reserve()是让您有机会在增加向量时围绕可能昂贵的重新分配进行编码。为了有用,您必须知道要存储的对象数量,或者至少需要能够做出有根据的猜测。如果不这样做,您最好远离,reserve()因为您只会更改浪费内存的重新分配。

So putting it all together:

所以把它们放在一起:

  • The standard intentionally does notspecify a constructor that allows you to pre allocate a memory block for a specific number of objects (which would be at least more desirable than allocating an implementation specific, fixed "something" under the hood).
  • Allocation shouldn't be implicit. So, to preallocate a block you need to make a separate call to reserve()and this need not be at the same place of construction (could/should of course be later, after you became aware of the required size to accomodate)
  • Thus if a vector would always preallocate a memory block of implementation defined size this would foil the intended job of reserve(), wouldn't it?
  • What would be the advantage of preallocating a block if the STL naturally cannot know the intended purpose and expected size of a vector? It'll be rather nonsensical, if not counter-productive.
  • The proper solution instead is to allocate and implementation specific block with the first push_back()- if not already explicitely allocated before by reserve().
  • In case of a necessary reallocation the increase in block size is implementation specific as well. The vector implementations I know of start with an exponential increase in size but will cap the increment rate at a certain maximum to avoid wasting huge amounts of memory or even blowing it.
  • 该标准故意没有指定允许您为特定数量的对象预先分配内存块的构造函数(这至少比在引擎盖下分配特定于实现的固定“东西”更可取)。
  • 分配不应是隐式的。因此,要预先分配一个块,您需要单独调用它,reserve()并且这不需要在同一个构造位置(可以/当然应该稍后,在您意识到要容纳的所需大小之后)
  • 因此,如果一个向量总是预先分配一个实现定义大小的内存块,这将阻碍 的预期工作reserve(),不是吗?
  • 如果 STL 自然无法知道向量的预期目的和预期大小,那么预分配块有什么好处?如果不是适得其反,那将是相当荒谬的。
  • 正确的解决方案是使用第一个分配和实现特定的块push_back()- 如果之前没有明确分配的话reserve()
  • 在必要的重新分配的情况下,块大小的增加也是特定于实现的。我所知道的向量实现开始时大小呈指数增长,但会将增量速率限制在某个最大值以避免浪费大量内存甚至耗尽内存。

All this comes to full operation and advantage only if not disturbed by an allocating constructor. You have reasonable defaults for common scenarios that can be overriden on demand by reserve()(and shrink_to_fit()). So, even if the standard does not explicitely state so, I'm quite sure assuming that a newly constructed vector does not preallocate is a pretty safe bet for all current implementations.

只有在不受分配构造函数干扰的情况下,所有这些才能完全运行并发挥优势。对于可以由reserve()(和shrink_to_fit())按需覆盖的常见场景,您有合理的默认值。所以,即使标准没有明确说明,我很确定假设一个新构造的向量不预分配对于所有当前实现来说是一个非常安全的赌注。

回答by David Woo

As a slight addition to the other answers, I found that when running under debug conditions with Visual Studio a default constructed vector will still allocate on the heap even though the capacity starts at zero.

作为对其他答案的一点补充,我发现在调试条件下使用 Visual Studio 运行时,即使容量从零开始,默认构造的向量仍会在堆上分配。

Specifically if _ITERATOR_DEBUG_LEVEL != 0 then vector will allocate some space to help with iterator checking.

特别是如果 _ITERATOR_DEBUG_LEVEL != 0 那么 vector 将分配一些空间来帮助迭代器检查。

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

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

I just found this slightly annoying since I was using a custom allocator at the time and was not expecting the extra allocation.

我只是觉得这有点烦人,因为我当时使用的是自定义分配器,并没有期待额外的分配。

回答by WhiZTiM

This is an old question, and all answers here have rightly explained the standard's point of view and the way you can get an initial capacity in a portable manner by using std::vector::reserve;

这是一个老问题,这里的所有答案都正确地解释了标准的观点以及您可以通过使用以便携方式获得初始容量的方式std::vector::reserve

However, I'll explain why it doesn't make sense for any STL implementation to allocate memory upon construction of an std::vector<T>object;

但是,我将解释为什么任何 STL 实现在构造std::vector<T>对象时分配内存都没有意义

  1. std::vector<T>of incomplete types;

    Prior to C++17, it was undefined behavior to construct a std::vector<T>if the definition of Tis still unknown at point of instantiation. However, that constraint was relaxed in C++17.

    In order to efficiently allocate memory for an object, you need to know its size. From C++17 and beyond, your clients may have cases where your std::vector<T>class does not know the size of T. Does it makes sense to have memory allocation characteristics dependent on type completeness?

  2. Unwanted Memory allocations

    There are many, many, many times you'll need model a graph in software. (A tree is a graph); You are most likely going to model it like:

    class Node {
        ....
        std::vector<Node> children; //or std::vector< *some pointer type* > children;
        ....
     };
    

    Now think for a moment and imagine if you had lots of terminal nodes. You would be very pissed if your STL implementation allocates extra memory simply in anticipation of having objects in children.

    This is just one example, feel free to think of more...

  1. std::vector<T>不完整的类型;

    在 C++17 之前,std::vector<T>如果在T实例化点的定义仍然未知,则构造 a 是未定义的行为。但是,该约束在 C++17 中放宽了

    为了有效地为对象分配内存,您需要知道它的大小。从 C++17 及更高版本开始,您的客户可能会遇到您的std::vector<T>类不知道T. 依赖于类型完整性的内存分配特性是否有意义?

  2. Unwanted Memory allocations

    很多时候,您需要在软件中对图形进行建模。(树是图);您最有可能将其建模为:

    class Node {
        ....
        std::vector<Node> children; //or std::vector< *some pointer type* > children;
        ....
     };
    

    现在想一想,想象一下您是否有很多终端节点。如果您的 STL 实现仅仅因为预期在children.

    这只是一个例子,请随意思考更多......

回答by Archie Yalakki

Standard doesnt specify initial value for capacity but the STL container automatically grows to accomodate as much data as you put in, provided you don't exceed the maximum size(use max_size member function to know). For vector and string, growth is handled by realloc whenever more space is needed. Suppose you'd like to create a vector holding value 1-1000. Without using reserve, the code will typically result in between 2 and 18 reallocations during following loop:

标准不指定容量的初始值,但 STL 容器会自动增长以容纳您放入的尽可能多的数据,前提是您不超过最大大小(使用 max_size 成员函数了解)。对于向量和字符串,只要需要更多空间,就会由 realloc 处理增长。假设您想创建一个包含值 1-1000 的向量。如果不使用保留,代码通常会在以下循环中导致 2 到 18 次重新分配:

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

Modifying the code to use reserve might result in 0 allocations during the loop:

修改代码以使用 Reserve 可能会在循环期间导致 0 次分配:

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

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

Roughly to say, vector and string capacities grow by a factor of between 1.5 and 2 each time.

粗略地说,向量和字符串容量每次增长 1.5 到 2 倍。