未使用的STL容器是否分配内存?

时间:2016-02-24 17:36:20

标签: c++ stl stdvector allocation stdmap

鉴于代码:

class Foo {
  std::vector<int> items;
  std::map<int, int> dictionary;
};
  1. 如果上述向量或地图中没有添加任何内容,是否还会分配一块缓冲区内存? (换句话说,缓冲区分配总是在容器创建期间发生,还是可以在调用push_back之类的函数之前推迟?)

  2. 是否有处理初始STL容器缓冲区分配时间的标准,或者是否允许STL容器和编译器之间的行为不同?

  3. 注意:这个问题不是关于这样的容器会增加到类Foo的大小的额外字节。

    (此问题的一个相关子集,强调分配大小为Initial capacity of vector in C++。)

5 个答案:

答案 0 :(得分:8)

C++ Reference对于C ++ 17,如果分配器构造为noexcept,则默认构造函数为noexcept。所以它取决于使用的分配器。在VS 2015中,标准构造函数为noexcept

澄清:这意味着如果分配器不是noexcept,则不会分配任何内存块。

对于你的第二个问题:相同的参考,它是O(1)。

答案 1 :(得分:5)

标准没有说明任何内容,但我专门研究的实现将为std::vector做一些预先分配,并且不会为{{1}预先分配任何内容。 }。

这实际上曾经让我困难,当我讨厌一个巨大的容器,哪些元素有一个微不足道 - 不超过10个元素,大多数条目有0大小的向量 - 矢量在其中。此实现中的默认向量容量为32,并且&#39; 32 * sizeof(vector_element)* number_of_elements&#39;碰巧是非常大的。

答案 2 :(得分:3)

如前所述,这个定义不明确。但是,你可以测试它。

例如使用gcc / linux。创建一个简单的程序,用-O0 -g编译它并在gdb中运行它。然后

break main
run
break malloc
cont

现在只需在每个malloc上运行backtrace,您就会看到动态分配。使用我的gcc 5.3.0,两个空容器都不分配堆内存,这是在第一个push_back / operator[]上完成的。

当然,如果不是gdb / malloc,您应该使用首选调试器并中断分配器底层函数。

现在,如果你考虑这两个案例。在这种情况下预先分配内存是否有意义?

std::vector<int> foo;
foo.push_back(13);

嗯,从技术上讲,你可以保存nullptr的检查,但是通常用三个指针来实现向量,不需要额外的检查。

但请考虑

std::vector<int> foo;
foo.reserve(100);

在这种情况下,预分配对性能有害。

我找不到为树结构预分配的参数,例如map。

请记住,这是一个非常具体的优化。只有充分的理由才能对此进行优化(基准!)。

注意:您可能希望阅读有关小字符串优化的信息,这是一种非常常见的相关但不同的技术。

答案 3 :(得分:2)

  
      
  1. 如果上述向量或地图中没有添加任何内容,是否仍会为潜在条目分配一块内存? (换句话说,在容器创建过程中是否总是会发生入口分配,或者在调用push_back之类的函数之前是否会延迟?)
  2.   

那可能发生,是的。它实际上是容器实现的细节,未在标准中指定。

  
      
  1. 是否有处理初始STL容器分配时间的标准,或者STL容器和编译器之间允许的行为是否有所不同?
  2.   

您可以使用例如推迟创作成员std::unique_ptr,并通过调用getter函数创建它们。

答案 4 :(得分:1)

值得注意的是,Microsoft的STL实现当前在 中分配了std::mapstd::set的默认构造函数。 Godbolt example-注意在程序集输出的第8行上对operator new的调用。

是的,这绝对会影响性能。在剖析了一些神秘的慢代码之后,我才意识到这一点,结果发现,具有很少使用的map成员的类的默认构造函数主导着所讨论的循环的运行时间。

如果像我一样,您对这个实施决定不完全满意,我会指出Boost.Container的同行do guarantee零分配默认结构。