标准是否保证std :: vector占用的总内存比例为C + N * sizeof(T)?

时间:2014-04-22 00:30:34

标签: c++ memory-management c++11 standards sizeof

C ++标准保证std :: vector的内容是连续存储的。但是它是否说明占用的总内存是:

S = C+N*sizeof(T)

其中:

  • S是堆栈上和堆上的总大小
  • C是堆栈上的总大小:C = sizeof(std :: vector)
  • N是向量的容量
  • T是存储的类型

换句话说,我是否保证每个元素没有开销? 如果我没有这样的保证,有什么理由吗?

编辑:要清楚,如果我以std::list为例,它通常会为每个元素存储2个额外的指针。所以我的问题是:std::vector的这种实现是否符合标准?

4 个答案:

答案 0 :(得分:11)

对于任何此类保证,标准必须将要求传递给分配器的接口。它没有,所以没有。

实际上,作为实现质量问题,您希望内存分配器每个分配可能有一个恒定的开销,但没有与分配大小成比例的开销。对此的反例是内存分配器,无论请求的大小如何,它总是使用两倍大小的块。这对于大型分配来说​​相当浪费,但不能作为用户定义的分配器或::operator new[]使用的系统分配器禁止。假设矢量容量不合适,它会产生与N成比例的开销。

除了分配器之外,我不相信标准中有任何东西可以说向量不能为每个元素分配(例如)一个额外的字节并使用它来存储一些标志以供谁知道什么用途。正如其他人所说,连续性要求意味着那些额外的字节不能位于向量元素之间。它们必须在分配的一端进行单独分配或一起进行。

至少有一个很好的理由是,该标准不会禁止通过使用它来存储用于标准不需要的操作的数据来“浪费”空间的实现 - 这样做会排除许多调试技术!

答案 1 :(得分:2)

  

我是否保证每个元素没有开销?

标准是否禁止它?第
但是你会期望在实践中看到这一点吗?否。

连续数据存储规则和向量增长的复杂性要求意味着非常大小数据块成为向量一部分的唯一可能方式是,如果它直接放在动态分配元素之前数据,或完全在其他地方。不能保证这不会发生,但是,很简单,没有实现它,因为它完全是荒谬的,没有任何目的。

  

是否说明占用的总内存为:

S = C+N*sizeof(T)

矢量本身可能还有其他数据成员(你不准确地认为是“在堆栈中”),以不变的方式增加对象的大小。

答案 2 :(得分:0)

该标准不保证,afaics。但是连续存储元素的要求使得很可能没有每个元素开销。整个数据必须位于一个分配的存储区中。 @aschepler正确地评论了虽然典型的免费商店实现每个分配单元有一个(常量)开销,通常是一个大小变量或一个结束指针。

此外,可能存在一些填充开销,例如分配单元可能会跨越机器上自然字大小的倍数。然后OS调用可能会为程序保留一个完整的内存页面,即使您只分配了1个字节。你是否认为开销是一个品味问题(从外部是的,从程序内部没有;当然后续的矢量或调整大小()从同一页面用餐)。

所以至少它是 CM + CV + N * sizeof(T), CM和CV是向量中的开销(不一定在堆栈上,如Lighness所说)和CM的开销内存管理。

答案 3 :(得分:0)

不,您建议的实施特性不符合标准。 STL指定std::vector支持在分摊的常量时间中附加单个元素。

为了使元素的分摊成本为O(1),数组的大小必须在重新分配时至少以几何级数增加(参见here)。几何级数表示如果数组的大小为N,则重新分配后的新大小必须为K * N,对于某些K > 1K的选择取决于实现。

要了解std::vector分配了多少空间,请致电std::vector::capacity()。关于每个元素的开销,最好的情况是capacity() == size()。在最糟糕的情况下capacity() == K * (size() - 1)

如果你必须确保你的矢量绝对不超过它,你可以打电话std::vector::reserve(),如果你确切知道std::vector的大小。完成添加元素以减少保留的内存量后,您也可以在{C ++ 11中调用std::vector::resize()(或std::vector::shrink_to_fit())。