C ++中的高效数组重新分配

时间:2012-01-12 02:37:47

标签: c++ memory-management allocator

如何有效地调整使用符合标准的C ++分配器分配的数组的大小?我知道C ++ alloctor界面中的no facilities for reallocation are provided,但C ++ 11版本是否使我们能够更轻松地使用它们?假设我有一个vec类,其中定义了一个复制赋值运算符foo& operator=(const foo& x)。如果x.size() > this->size(),我被迫

  1. foo
  2. 的内部存储空间中的所有元素调用allocator.destroy()
  3. foo.
  4. 的内部存储上调用allocator.deallocate()
  5. 重新分配一个新的缓冲区,为x.size()个元素留出足够的空间。
  6. 使用std :: uninitialized_copy填充存储空间。
  7. 有没有什么方法可以更轻松地重新分配foo的内部存储空间而无需完成所有这些操作?如果您认为它有用,我可以提供一个实际的代码示例,但我觉得这里没有必要。

6 个答案:

答案 0 :(得分:3)

基于previous question,我用来处理可以合理有效地增长和缩小的大型数组的方法是编写一个类似于deque的容器,将数组分解为多个较小数组的页面。例如,假设我们有一个n个元素的数组,我们选择一个页面大小p,并创建p个元素的1 + n / p数组(页面)。当我们想要重新分配和增长时,我们只需将现有页面保留在原来的位置,然后分配新页面。当我们想缩小时,我们释放完全空的页面。

缺点是数组访问稍慢,在给定和索引i中,你需要page = i / p,以及页面i%p的偏移量来获取元素。我发现这仍然非常快,但提供了一个很好的解决方案。从理论上讲,std :: deque应该做一些非常相似的事情,但对于我尝试使用大型数组的情况,它非常慢。有关详细信息,请参阅关联问题的评论和注释。

在给定n个元素的情况下,内存效率也很低,我们总是保留p-n%p个元素。即我们只分配或取消分配完整的页面。这是我在大型阵列环境中提出的最佳解决方案,需要重新调整大小和快速访问,而我不怀疑有更好的解决方案,我很乐意看到它们。

答案 1 :(得分:1)

  

如果x.size() > this->size()中有foo& operator=(foo&& x),也会出现类似的问题。

不,它没有。你只需swap

答案 2 :(得分:1)

没有功能会在适当的位置调整大小或在失败时返回0(调整大小)。我不知道任何支持这种功能的操作系统,除了告诉你实际上特定的分配有多大。

但是,所有操作系统都支持实现realloc,但如果无法在适当位置调整大小,则会执行复制。

所以,你不能拥有它,因为如果必须添加一个标准函数,C ++语言就无法在大多数当前操作系统上实现。

答案 3 :(得分:0)

C++11 rvalue reference and move constructors

他们有一个很棒的video talk

答案 4 :(得分:0)

即使存在重新分配,实际上,您只能在复制构造函数中避免在问题中提到的#2。但是,在内部缓冲区增长的情况下,重新分配可以保存这四个操作。

  1. 阵列的内部缓冲区是否连续?如果是这样,请参阅answer of your link
  2. 如果没有,Hashed array treearray list可能是您避免重新分配的选择。

答案 5 :(得分:0)

有趣的是,只要在初始分配的缓冲区结束后有足够的未使用空间,g ++的默认分配器就足够智能,可以为连续的解除分配和更大的分配使用相同的地址。虽然我还没有测试过我要宣称的内容,但我怀疑malloc / realloc和allocate / deallocate / allocate之间存在很大的时间差异。

如果您知道当前缓冲区之后有足够的空间,以便重新分配不会产生新地址,则会导致可能非常危险的非标准快捷方式。 (1)在不调用alloc.destroy()的情况下释放当前缓冲区(2)分配一个新的,更大的缓冲区并检查返回的地址(3)如果新地址等于旧地址,则继续愉快;否则,您丢失了数据(4)为新分配的空间中的元素调用allocator.construct()。

除了满足你自己的好奇心之外,我不会提倡使用它,但它确实适用于g ++ 4.6。