为什么shrink_to_fit(如果请求得到满足)会导致重新分配?

9

假设有一个容器v,使得v.size() == 3v.capacity() == 5,我的理解是调用v.shrink_to_fit() 可以实现目的,如果调用成功,则会导致v.capacity()变为3。

然而,这样做会带来重新分配内存的代价。

为什么呢?不能在不重新分配剩余内存的情况下释放未使用的内存吗?

可能这个问题源于像new/deletemalloc/free这样的更原始命令的工作方式。


@largest_prime_is_463035818,我在我的问题中澄清了“can”的含义。我认为(但有人可能会纠正我)shrink_to_fit _can_重新分配内存,因为请求_can_得到满足。也就是说,调用shrink_to_fit可能不执行任何操作,但如果需要,它会通过重新分配来进行。 - Enlico
1
抱歉删除了我的评论,因为我看到了你的编辑,并且我不确定“可能重新分配”是否取决于“可能缩小”。一开始认为不是,但后来在标准中没有找到相关部分。 - 463035818_is_not_a_number
哦,我希望我能更加专注于这个。 - Enlico
3个回答

6
底层内存管理系统定义了可能性,通常不允许返回已分配内存的部分:如果你获得了n字节,你只能返回n字节或不返回。
返回最后m 字节(其中m < n)或更糟糕的是,在n字节的中间返回m字节,当然是可以提供的,但考虑到正确处理所需的额外复杂度。
当然,有些内存管理器可能支持此功能,但你的C++编译器和语言定义并不一定知道在操作系统下运行的哪些管理器,因此它们必须接受需要重新分配的可能性。注意,它们并不保证需要重新分配 - 它们只预期需要。

5

容器本身不分配/释放内存,而是其分配器进行分配/释放内存。

为了使(向量的)分配器能够释放内存,它需要提供与已分配给向量数据的内存指针完全相同的指针。

这就是向量数据的开头,而不是“不再使用”的数据的开头

基本上,我们正在谈论这个分配器的分配/释放方法:

pointer allocate( size_type n, const void * hint = 0 );
void deallocate( T* p, std::size_t n );
deallocate 的参数 T* p 将与从 allocate 返回的指针相同 (== 向量数据的开始).这就是向量实现将传递给 deallocate 的内容。
可以想象,有一个自定义的向量实现,能够将范围在 [data, data+size] 中的任何指针传递给分配器的 deallocate 方法。可以构造这样一个分配器来处理它。但是,所有其他分配器都需要遵守此 API,包括标准分配器。
然后像这样的东西需要能够 "工作":
int* p = new int[100];
delete [] (p + 50);  // imagine making this work

这将增加额外的复杂性、性能和其他问题。

1
事实上,标准只是允许分配器在收缩时重新分配。我不知道C++库实现中是否常见,但我记得有一些数据库分配器使用不同的磁盘段池,一些用于较小的块,一些用于较大的块。理由是有很多小块和少量大块,因此这种分离有助于减少较大块池的碎片。如果程序中发生这种情况,定义一个实现该规则的自定义分配器可能是有意义的。
事实上,标准表明“shrink_to_fit”可能会重新分配,这样就可以使用这样的自定义分配器。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接