该标准是否保证std::vector占用的总内存按C+N*sizeof(T)缩放? C++标准提供了保证STD::向量的内容是连续存储的保证。但它是否说明占用的内存总量为: S = C+N*sizeof(T) S = C+N*sizeof(T)

该标准是否保证std::vector占用的总内存按C+N*sizeof(T)缩放? C++标准提供了保证STD::向量的内容是连续存储的保证。但它是否说明占用的内存总量为: S = C+N*sizeof(T) S = C+N*sizeof(T),c++,memory-management,c++11,standards,sizeof,C++,Memory Management,C++11,Standards,Sizeof,其中: S是堆栈和堆上的总大小 C是堆栈上的总大小:C=sizeofstd::vector N是向量的容量 T是存储的类型 换句话说,我是否可以保证每个元素没有开销? 如果我没有这样的保证,有什么理由吗 编辑:要清楚,如果我以std::list为例,它通常为每个元素存储2个额外的指针。所以我的问题是:这样的std::vector实现是否符合标准 我是否可以保证每个元素没有开销 标准禁止吗?不 但你会期望在实践中看到这一点吗?没有 连续数据存储规则和向量增长的复杂性要求意味着,非恒定大小的数据块成

其中:

S是堆栈和堆上的总大小 C是堆栈上的总大小:C=sizeofstd::vector N是向量的容量 T是存储的类型 换句话说,我是否可以保证每个元素没有开销? 如果我没有这样的保证,有什么理由吗

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

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

标准禁止吗?不 但你会期望在实践中看到这一点吗?没有

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

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

S = C+N*sizeof(T)
S = C+N*sizeof(T)
向量本身可能有其他数据成员,您错误地认为它们在堆栈上,以常量增加对象的大小

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

标准禁止吗?不 但你会期望在实践中看到这一点吗?没有

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

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

S = C+N*sizeof(T)
S = C+N*sizeof(T)

向量本身可能有其他数据成员,您错误地认为它们在堆栈上,以常量增加了对象的大小。

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

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

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


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

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

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

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


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

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

此外,可能存在一些填充开销,例如,分配单元可能会跨越机器上自然字长的倍数。然后,操作系统调用可能会为程序保留一个完整的内存页,即使您只分配了1个字节。不管你认为这是不是开销,都是来自外部的问题,是的,从程序的内部看不到。当然,后续的向量或大小调整都来自同一页


因此,至少它是CM+CV+N*sizeofT,CM和CV是向量中的开销,而不一定是堆栈上的开销,正如Lighness所说,CM是内存管理的开销。

标准没有保证,afaics。但是元素被连续存储的要求使得很可能没有每个元素的开销。整个数据必须在一块分配的内存区域中@aschepler正确地指出,尽管典型的免费商店实现在每个分配单元上都有恒定的开销,通常是一个大小变量或一个结束指针

此外,可能存在一些填充开销,例如,分配单元可能会跨越机器上自然字长的倍数。然后,操作系统调用可能会为程序保留一个完整的内存页,即使您只分配了1个字节。不管你认为这是不是开销,都是来自外部的问题,是的,从程序的内部看不到。当然,后续的向量或大小调整都来自同一页


因此,至少它是CM+CV+N*sizeofT,CM和CV是向量中的开销,而不是堆栈上的开销,正如Lighness所说,CM是内存管理的开销。

不,您建议的实现特性不符合标准。STL指定std::vector support在摊销的固定时间内附加单个元素

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

要了解std::vector分配了多少空间,请调用std::vector::capacity。关于每个元素的开销,在最佳情况下,容量==大小。在最坏的情况下,容量==K*size-1


如果您必须确保您的向量绝对不大于它必须的大小,那么如果您确切知道您的std::vector有多大,您可以调用std::vector::reserve。在添加完元素以减少保留的内存量后,您还可以调用std::vector::resize或std::vector::shrink_以适应C++11。否,您建议的实现特征不符合标准。STL指定std::vector support在摊销的固定时间内附加单个元素

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

要了解std::vector分配了多少空间,请调用std::vector::capacity。关于每个元素的开销,在最佳情况下,容量==大小。在最坏的情况下,容量==K*size-1


如果您必须确保您的向量绝对不大于它必须的大小,那么如果您确切知道您的std::vector有多大,您可以调用std::vector::reserve。在添加完元素以减少保留的内存量后,您还可以调用std::vector::resize或std::vector::shrink_以适应C++11。

@Mehrdad:您能详细说明一下吗?在重新分配内存时,它的大小通常是当前大小的两倍。因此,在这种情况下,最坏情况下的场景向量可以容纳的大小几乎是当前元素数量的两倍。2N*sizeoft典型的分配系统有隐藏的开销,但通常它会随着分配块的数量而扩展,而不是分配块的大小


阻碍。不确定Mehrdad的意思。请注意,N不是std::vector::size,而是std::vector::capacity。你的话对Mehrdad有用吗?@Vincent:当然,但这是一个糟糕的实现,无缘无故地浪费你的内存。@Mehrdad:你能详细说明一下吗?在重新分配内存时,它的大小通常是当前的两倍。因此,在这种情况下,最坏情况下的情况向量的大小几乎是当前元素数量的两倍。2N*sizeoft典型的分配系统具有隐藏开销,但通常它随分配块的数量而不是分配块的大小而扩展。不确定Mehrdad的意思。请注意,N不是std::vector::size,而是std::vector::capacity。你的话对Mehrdad有影响吗?@Vincent:当然,但这是一个糟糕的实现,无缘无故地浪费你的内存。这取决于总内存占用的定义。如果它是由分配的总大小定义的,那么这是一个独立的要求。出于效率原因,常量不太可能:使用更多内存来避免碎片。斐波那契和其他低碎片方案。@Mehrdad:嗯,它的定义并不完全是这样的,因为它被定义为在堆栈上包含一些内存。但是可以肯定的是,如果它是这样定义的,那么仍然没有明确的保证,但是很难为开销找到一个合理的位置。我不认为它定义为在堆栈上包含一些内存是正确的。考虑新的矢量。@ Lexness CraceSein轨道:精确地定义了这种方式。也许是错误的,或者提问者意识到他只是在谈论自动变量。C是堆栈上的总大小:C=sizeofstd::vector。但是,即使考虑到新的向量,所讨论的空间仍然不是要分配的总大小的一部分,因此,通过适当的调整,我认为Mehrdad的观点是正确的。我想您可以先使用std::allocator::allocate,然后再使用emplace,这样就可以在一个分配器下实现所有功能。这取决于如何定义占用的总内存。如果它是由分配的总大小定义的,那么这是一个独立的要求。出于效率原因,常量不太可能:使用更多内存来避免碎片。斐波那契和其他低碎片方案。@Mehrdad:嗯,它的定义并不完全是这样的,因为它被定义为在堆栈上包含一些内存。但是可以肯定的是,如果它是这样定义的,那么仍然没有明确的保证,但是很难为开销找到一个合理的位置。我不认为它定义为在堆栈上包含一些内存是正确的。考虑新的矢量。@ Lexness CraceSein轨道:精确地定义了这种方式。也许是错误的,或者提问者意识到他只是在谈论自动变量。C是堆栈上的总大小:C=sizeofstd::vector。但是,即使考虑到新的向量,所讨论的空间仍然不是要分配的总大小的一部分,因此,通过适当的调整,我认为Mehrdad的观点是正确的。我想你可以先使用std::allocator::allocate,然后再使用emplace,这样就可以将所有内容都放在一个分配器下。仅供参考,在内存的哪个部分存储了生成sizeofT结果的元素,例如,通常是std::vector的三个指针:静态、堆栈或堆?@Deduplicator:这段文字指的是向量的直接成员,其组合大小必须是常量,就像任何类型的情况一样。添加随机+42字节将需要动态分配,答案的其余部分将对此进行说明。仅供参考,构成sizeofT结果的元素存储在内存的哪一部分?例如,std::vector通常有3个指针:静态、堆栈或堆?@Deduplicator:该段落指的是向量的直接成员,其组合大小必须是常量,就像任何类型的情况一样。添加随机+42字节将需要动态分配,答案的其余部分将对此进行说明。为什么他不总是分配第二个大小为C的块?这很疯狂,但不能保证他不会这么做。-1 re但是元素连续存储的要求意味着没有每个元素的开销。这是错误的。不要求std::vector将其所有状态存储在主项缓冲区的用户可见部分。@Alf:严格说来是真的。我改为可能;-。它通过list等@Deduplicator排除病态实现,因为它不能保证是连续的。事实上,在典型的实现中,由于插入了alloc块大小信息,它保证是不连续的。@PeterSchneider:对不起,我不太确定。为什么实现只是为了好玩而浪费的额外内存会持续到其他任何东西呢?为什么他不总是分配第二个大小为C的块?这很疯狂,但不能保证他不会这么做。-1 re但是元素连续存储的要求意味着没有每个元素的开销。这是错误的。std::vector不需要存储al
l它在主项缓冲区的用户可见部分的状态。@Alf:严格说来是真的。我改为可能;-。它通过list等@Deduplicator排除病态实现,因为它不能保证是连续的。事实上,在典型的实现中,由于插入了alloc块大小信息,它保证是不连续的。@PeterSchneider:对不起,我不太确定。为什么实现只是为了好玩而浪费的额外内存与其他任何东西都是连续的呢?在这个问题中,N已经是向量的容量,而不是大小。@SteveJessop:是的,似乎我误解了OP的问题。@SteveJessop:虽然OP也声称他的规范没有每个元素的开销,这对我来说似乎很自然地解释为数据元素,而不是整个分配的容量。在问题中,N已经是向量的容量,而不是大小。@SteveJessop:是的,似乎我误解了OP的问题。@SteveJessop:虽然OP也声称他的规范没有每个元素的开销,这对我来说似乎很自然地解释为数据元素,而不是整个分配的容量。