标准是否保证std::向量占用的总内存按C+N*sizeof(T)缩放

Does the standard guarantee that the total memory occupied by a std::vector scales as C+N*sizeof(T)?

本文关键字:sizeof C+N 缩放 内存 是否 std 向量 标准      更新时间:2023-10-16

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

S = C+N*sizeof(T)

其中:

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

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

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

为了有任何这样的保证,标准必须将要求传递到分配器的接口。没有,所以没有。

然而,在实践中,作为一个实现质量问题,您预计内存分配器每次分配可能有恒定的开销,但没有与分配大小成比例的开销。一个反例是内存分配器,它总是使用大小为2的幂的块,而不管请求的大小如何。对于大型分配来说,这将是非常浪费的,但作为用户定义的分配器,甚至作为::operator new[]使用的系统分配器,都不会被禁止。它将产生一个平均与N成比例的开销,假设矢量容量恰好不适合。

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

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

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

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

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

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

S = C+N*sizeof(T)

向量本身可能还有其他数据成员(你不准确地认为是"在堆栈上"的数据),从而不断增加对象的大小。

标准不提供任何保证。但是,元素连续存储的要求使得每个元素可能没有开销。整个数据必须在一个内存区域中,该内存区域被分配为一个片段@aschepler正确地指出,典型的免费存储实现每个分配单元都有(恒定的)开销,通常是一个大小变量或结束指针。

此外,还可能存在一些填充开销,例如,分配单元可能跨越机器上自然单词大小的倍数。然后操作系统调用可能会为程序保留整个内存页,即使您只分配了1个字节。是否将其视为开销取决于品味(从外部是,从程序内部否;当然,后续向量或resize()s来自同一页)。

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

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

为了使插入元素的摊余成本为O(1),当重新分配时,数组的大小必须至少以几何级数增加(请参见此处)。几何级数意味着,如果数组的大小是N,则对于某些K > 1,重新分配后的新大小必须是K * NK的选择取决于实现。

要了解std::vector分配了多少空间,请调用std::vector::capacity()。关于每个元素的开销,在最佳情况下,capacity() == size()。在最坏的情况下CCD_ 13。

如果你必须确保你的向量绝对不大于它必须的大小,如果你确切地知道你的std::vector有多大,你可以调用std::vector::reserve()。你也可以在添加完元素后调用std::vector::resize()(或C++11中的std::vector::shrink_to_fit()),以减少保留的内存量。