总是在realloc上推荐std :: vector是否公平?

时间:2014-04-08 09:31:46

标签: c++ realloc

来自Bjarne Stroustrup's FAQ

  

如果你觉得需要realloc() - 还有很多人 - 那么考虑使用   标准库矢量。

我将通过同意std::vector出于多种原因更好地解释我的问题,并且我个人总是会选择使用它来编写我自己的带有C内存分配的动态数组。

std::vector会在内存增长时对内存进行分段,因为C ++没有等同于realloc编辑为了澄清,我知道{{ 1}}的存储是连续的,不会碎片化,我的意思是分配和解除分配导致的内存空间碎片,std::vector可以通过扩展现有的分配来避免。那么总是推荐它realloc是否公平?非常小心,你不能写一些像realloc那样有效但使用C分配函数的东西,它有可能在不移动地址和复制现有元素的情况下增加内存,使其成为好或更好的术语碎片和表现?

相关(奖金问题!),为什么 C ++没有等同于std::vector?在一种如此专注于性能的语言中省略这似乎是一件奇怪的事情。 Bjarne常见问题解答中的部分正好具有该标题(减去重点),但答案并未解决'为什么'。这只是偶然的遗漏吗?与realloc / new的工作原理有什么根本的不一致吗?它是否真的没有给它在实践中带来的好处?

修改:好的,所以我忽略了delete - realloc的C nastiness无法使用std::vector重写,因为它只能起作用与PODs,不扔等等。在某些情况下,为了处理恶劣而编写的POD专用容器可能是个好主意。但无论如何,更有趣的问题就变成了:realloc会从std::vector的C ++中获益,它在这里得到了(或多或少)的答案:

Does std::vector *have* to move objects when growing capacity? Or, can allocators "reallocate"?

可悲的是,答案似乎是“是的,但标准委员会没有投票”。这是希望。

4 个答案:

答案 0 :(得分:3)

直接比较

                        |  std::vector     | C memory functions
------------------------+------------------+------------------------
default capacity        | undefined        | undefined
default grow            | towards capacity | undefined
deterministic capacity  | available        | no
deterministic grow      | available        | no
deterministic mem.-move | available        | no
non-POD types           | yes              | f***ing no (*)
no-throw                | no               | yes

deterministic mem.-move来自deterministic capacity/grow。当reallocstd::vector必须将其存储的元素移动到新的内存位置时。

当你考虑移动(智能)任何类型的引用时,我认为关于内存移动的(可用)确定性是非常重要的。

注意:在这方面,我使用术语" deterministic"关于我的源代码生存期,即它在具有不同编译标志的不同库的不同版本中的生命周期等。


来源

它会像realloc那样对内存进行分片处理:

  

类模板矢量概述[vector.overview]

     

a的元素   向量是连续存储的,这意味着如果vvector<T, Allocator>,其中T是除bool以外的某种类型,那么它会服从所有&v[n] == &v[0] + n的{​​{1}} {1}}

换句话说,所使用的内存是一体的。

一个很大的区别是0 <= n < v.size()实际上可以增加分配的内存部分而不是命令它这样做,但是,它不需要这样做(realloc):

  

man 3 realloc

     

realloc()函数将ptr指向的内存块的大小更改为size字节。内容会          在从区域的起点到新旧尺寸的最小值的范围内保持不变。如果是新的          size大于旧的大小,添加的内存不会被初始化。如果ptr为NULL,则调用为          对于所有大小值,等效于malloc(大小);如果size等于零,并且ptr不为NULL,则调用          相当于free(ptr)。除非ptr为NULL,否则必须由之前调用malloc()返回,          loc()或realloc()。 如果指向的区域被移动,则完成免费(ptr)。

因此它可以增加大小,但不是必需的。

man 3 realloc不仅包含std::vector,还包含size。如果你事先知道你需要一个大的capacity,但你现在无法初始化所有内容,你就有权像这样增加你的载体的容量:

vector

因此,与std::vector<T> vec(32); vec.reserve(1024); // vec has size 32, but reserved a memory region of 1024 elements 不同,重新分配的时刻可以是realloc的确定性。

回答您的问题:因为std::vector,所以不需要std::vector。并且,非POD类型不允许realloc;尝试直接在非POD上使用reallocmallocfree会产生未定义的行为。

答案 1 :(得分:3)

new / new[]delete / delete[]通常位于C库分配函数的顶层(ala malloc / realloc / { {1}}),可能有一个额外的小对象优化层,使用一个free - ed区域来快速满足许多小malloc个请求。这种分层意味着支持newnew对早期C ++库作者的实施工作很少。

要在delete中使用C ++中的就地调整大小功能,需要对realloc库函数进行侵入性更改,以便在移动到新内存区域 required,C ++库代码有机会复制构造/销毁被移动的对象。这可以这样做:

  • realloc意识到需要移动之后发生回调,要求C ++库执行实际数据移动而不是执行realloc样式字节副本,或

    < / LI>
  • 作为额外的resize-place-or-fail-without-moving函数,因此C ++库代码可以尝试,然后在删除之前返回到memcpy()和正确/安全的副本原始对象并释放原始记忆。

由于大多数C库malloc函数缺少任何此类挂钩/查询功能,因此C ++标准版和标准库并不需要它。正如Mehrdad所指出的那样,this answer记录了SGI对此问题的认可。

鉴于目前C ++的广泛使用,恕我直言 - 在C ++库本身提供realloc / malloc / realloc实现是有意义的钩子/查询,以便在free中看到实用程序的C ++库作者可以自由地使用它;这将是一个值得纳入未来标准的候选人。

  

非常小心,你不能写一些像std :: vector一样的东西,但是使用C分配函数,它有可能在不移动地址和复制现有元素的情况下增加内存,使其成为好的在碎片化和性能方面还是更好?

如上所述 - 不 - 如果不对realloc API进行更改,就无法复制构造/破坏对象。

答案 2 :(得分:2)

  

std::vector在内存增长时对内存进行分段,因为C ++没有等效的realloc

realloc将以相同的方式对内存进行分段,因为它正在执行相同的操作 - 如果旧块不够大,则分配新块并复制内容。

  

非常小心,你不能写一些像std::vector那样有效但使用C分配函数的东西,它有可能在不移动地址和复制现有元素的情况下增加内存,使其成为好或在碎片化和性能方面更好?

这正是vector的作用。您可以独立于 size 控制容量,并且只有在超出容量时才会重新分配。 realloc类似,但没有办法控制容量 - 使得vector在碎片和性能方面更好。

  

为什么C ++不能等同于realloc

因为它有std::vector,它具有更大的灵活性。除了允许您精确控制内存的分配方式和时间外,它适用于任何可移动类型,而realloc在与非平凡类型一起使用时会出现严重错误。

答案 3 :(得分:2)

C ++没有realloc的理由很充分;而不是重复它,我会指出你的答案here

为了记录,正确的vector实施在某种程度上缓解了碎片问题,by choosing a growth factor close to the golden ratio,所以它并非完全是一个失败的原因。