如果你觉得需要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"?
可悲的是,答案似乎是“是的,但标准委员会没有投票”。这是希望。
答案 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
。当realloc
和std::vector
必须将其存储的元素移动到新的内存位置时。
当你考虑移动(智能)任何类型的引用时,我认为关于内存移动的(可用)确定性是非常重要的。
注意:在这方面,我使用术语" deterministic"关于我的源代码生存期,即它在具有不同编译标志的不同库的不同版本中的生命周期等。
它会像realloc
那样对内存进行分片处理:
类模板矢量概述[vector.overview]
a的元素 向量是连续存储的,这意味着如果
v
是vector<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上使用realloc
,malloc
和free
会产生未定义的行为。
答案 1 :(得分:3)
new
/ new[]
和delete
/ delete[]
通常位于C库分配函数的顶层(ala malloc
/ realloc
/ { {1}}),可能有一个额外的小对象优化层,使用一个free
- ed区域来快速满足许多小malloc
个请求。这种分层意味着支持new
和new
对早期C ++库作者的实施工作很少。
要在delete
中使用C ++中的就地调整大小功能,需要对realloc
库函数进行侵入性更改,以便在移动到新内存区域时 required,C ++库代码有机会复制构造/销毁被移动的对象。这可以这样做:
在realloc
意识到需要移动之后发生回调,要求C ++库执行实际数据移动而不是执行realloc
样式字节副本,或
作为额外的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,所以它并非完全是一个失败的原因。