C + + 中向量的初始容量

使用默认构造函数创建的 std::vectorcapacity()是多少?我知道 size()是零。我们是否可以声明默认构造的向量不调用堆内存分配?

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

For example, on Linux (g++ 4.4.5, kernel 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?

75897 次浏览

标准没有指定容器的初始 capacity应该是什么,所以您依赖于实现。一个通用的实现将从零开始容量,但是没有保证。另一方面,没有办法改善你的 std::vector<int> iv; iv.reserve(2345);战略,所以坚持它。

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

cin.get();
return 0;
}

Gives the following output:

0
1
2
4
4
8
8
8
8
16
16

根据海湾合作委员会5.1,11.2-Clang 12.0.1及:

0
1
2
3
4
6
6
9
9
9
13

根据2013年 MSVC。

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

具体来说,如果 _ ITERATION _ DEBUG _ LEVEL! = 0,那么 Vector 将分配一些空间来帮助进行迭代器检查。

Https://learn.microsoft.com/en-gb/cpp/standard-library/iterator-debug-level

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

Standard 没有指定容量的初始值,但是 STL 容器会自动增长,以容纳所放入的数据,前提是不超过最大容量(使用 max _ size 成员函数了解)。 对于向量和字符串,每当需要更多空间时,realloc 都会处理增长。假设您想创建一个保持值为1-1000的向量。如果不使用 reserve,代码通常会导致 在下列循环期间进行2次和18次重新分配:

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

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

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


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

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

就我对标准的理解而言(尽管我实际上不能命名一个引用) ,容器实例化和内存分配有意地解耦是有充分理由的。因此,你有不同的,单独的要求

  • constructor创建容器本身
  • reserve()预先分配一个适当大的内存块,以容纳至少(!)一个给定数量的对象

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

所以把它们放在一起:

  • 该标准有意为 不是指定了一个构造函数,允许您为特定数量的对象预先分配一个内存块(这至少比在引擎盖下分配一个特定于实现的、固定的“某些东西”更可取)。
  • 分配不应该是隐式的。因此,为了预分配一个块,你需要单独调用 reserve(),而且这不需要在同一个构造地点(可以/当然应该是以后,在你意识到需要容纳的大小之后)
  • 因此,如果一个向量总是预先分配一个实现定义大小的内存块,这将阻碍 reserve()的预期工作,不是吗?
  • 如果 STL 自然不能知道向量的预期用途和预期大小,那么预先分配一个块有什么好处?即使不会适得其反,也是相当荒谬的。
  • 相反,正确的解决方案是使用第一个 push_back()分配和实现特定的块——如果之前还没有由 reserve()显式分配的话。
  • 在必要的重新分配情况下,块大小的增加也是实现特有的。我所知道的向量实现从指数级增长开始,但是会将增长速率限制在一个特定的最大值,以避免浪费大量内存,甚至浪费内存。

只有在不受分配构造函数干扰的情况下,所有这些才能实现完全操作和优势。对于常见场景,您有合理的默认值,可以根据需要由 reserve()(和 shrink_to_fit())覆盖。因此,即使标准没有明确地指出这一点,我也可以肯定,假设一个新构建的向量没有预分配,对于所有当前的实现来说,这是一个相当安全的选择。

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;

然而,我将解释为什么 对于任何 STL 实现来说,在构造 std::vector<T>对象时分配内存是没有意义的;

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

    在 c + + 17之前,如果未定义行为实例化时还不知道 T的定义,就需要构造一个 std::vector<T>然而,这个限制在 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

    在软件中有很多很多次你需要建模一个图形。(树是一个图形) ; 你很可能会这样建模:

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

    现在想象一下,如果有很多终端节点。如果您的 STL 实现仅仅为了预期在 children中有对象而分配额外的内存,您会非常生气。

    这只是一个例子,随便想想..。