为什么 C++ 中没有重新分配功能?分配器?
在 C 中,标准内存处理函数是 malloc()
、realloc()
和 free()
。然而,C++ stdlib 分配器仅并行其中两个:没有重新分配函数。当然,不可能与realloc()完全相同,因为简单地复制内存并不适合非聚合类型。但是,例如,此函数是否会存在问题:
bool reallocate (pointer ptr, size_type num_now, size_type num_requested);
其中
ptr
之前已使用相同的分配器为num_now
对象分配了;num_requested
>=num_now
;
语义如下:
- 如果分配器可以将
ptr
处的给定内存块从num_now
对象的大小扩展到num_requested
对象,那么它就会这样做(留下额外的内存块)内存未初始化)并返回 true ; - 否则它什么都不做并返回
false
。
当然,这不是很简单,但据我了解,分配器主要用于容器,而容器的代码通常已经很复杂了。
给定这样一个函数,std::vector 可以按如下方式增长(伪代码):
if (allocator.reallocate (buffer, capacity, new_capacity))
capacity = new_capacity; // That's all we need to do
else
... // Do the standard reallocation by using a different buffer,
// copying data and freeing the current one
无法完全更改内存大小的分配器可以通过无条件返回 false 来实现这样的函数; 。
可重新分配的分配器实现是否太少以至于不值得去打扰?还是有一些我忽略的问题?
In C the standard memory handling functions are malloc()
, realloc()
and free()
. However, C++ stdlib allocators only parallel two of them: there is no reallocation function. Of course, it would not be possible to do exactly the same as realloc()
, because simply copying memory is not appropriate for non-aggregate types. But would there be a problem with, say, this function:
bool reallocate (pointer ptr, size_type num_now, size_type num_requested);
where
ptr
is previously allocated with the same allocator fornum_now
objects;num_requested
>=num_now
;
and semantics as follows:
- if allocator can expand given memory block at
ptr
from size fornum_now
objects tonum_requested
objects, it does so (leaving additional memory uninitialized) and returnstrue
; - else it does nothing and returns
false
.
Granted, this is not very simple, but allocators, as I understand, are mostly meant for containers and containers' code is usually complicated already.
Given such a function, std::vector
, say, could grow as follows (pseudocode):
if (allocator.reallocate (buffer, capacity, new_capacity))
capacity = new_capacity; // That's all we need to do
else
... // Do the standard reallocation by using a different buffer,
// copying data and freeing the current one
Allocators that are incapable of changing memory size altogether could just implement such a function by unconditional return false;
.
Are there so few reallocation-capable allocator implementation that it wouldn't worth it to bother? Or are there some problems I overlooked?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
从:
http://www.sgi.com/tech/stl/alloc.html" sgi.com/tech/stl/alloc.html
From:
http://www.sgi.com/tech/stl/alloc.html
这实际上是 Alexandrescu 指出的标准分配器的设计缺陷(不是运算符 new[]/delete[],而是最初用于实现 std::vector 的 stl 分配器,例如)。
realloc 的发生速度明显快于 malloc、memcpy 和 free。然而,虽然可以调整实际内存块的大小,但它也可以将内存移动到新位置。在后一种情况下,如果内存块由非 POD 组成,则在重新分配后,所有对象都需要被销毁并复制构造。
标准库需要适应这种情况的主要内容是作为标准分配器公共接口一部分的重新分配函数。像 std::vector 这样的类当然可以使用它,即使默认实现是 malloc 新大小的块并释放旧的块。它需要是一个能够销毁和复制构造内存中对象的函数,但如果它这样做,它就不能以不透明的方式处理内存。这里涉及到一点复杂性,并且需要更多的模板工作,这可能就是它被从标准库中省略的原因。
std::vector<...>::reserve 是不够的:它解决了可以预期容器大小的不同情况。对于真正可变大小的列表,realloc 解决方案可以使像 std::vector 这样的连续容器更快,特别是如果它可以处理内存块在不移动的情况下成功调整大小的情况,在这种情况下它可以省略调用 copy内存中对象的构造函数和析构函数。
This is actually a design flaw that Alexandrescu points out with the standard allocators (not operator new[]/delete[] but what were originally the stl allocators used to implement std::vector, e.g.).
A realloc can occur significantly faster than a malloc, memcpy, and free. However, while the actual memory block can be resized, it can also move memory to a new location. In the latter case, if the memory block consists of non-PODs, all objects will need to be destroyed and copy-constructed after the realloc.
The main thing the standard library needs to accommodate this as a possibility is a reallocate function as part of the standard allocator's public interface. A class like std::vector could certainly use it even if the default implementation is to malloc the newly sized block and free the old one. It would need to be a function that is capable of destroying and copy-constructing the objects in memory though, it cannot treat the memory in an opaque fashion if it did this. There's a little complexity involved there and would require some more template work which may be why it was omitted from the standard library.
std::vector<...>::reserve is not sufficient: it addresses a different case where the size of the container can be anticipated. For truly variable-sized lists, a realloc solution could make contiguous containers like std::vector a lot faster, especially if it can deal with realloc cases where the memory block was successfully resized without being moved, in which case it can omit calling copy constructors and destructors for the objects in memory.
您所要求的本质上就是
vector::reserve
所做的。如果没有对象的移动语义,就无法在不进行复制和销毁的情况下重新分配内存和移动对象。What you're asking for is essentially what
vector::reserve
does. Without move semantics for objects, there's no way to reallocate memory and move the objects around without doing a copy and destroy.我想这是上帝出错的事情之一,但我只是懒得写信给标准委员会。
应该有一个用于数组分配的 realloc:
p = renew(p) [128];
或类似的东西。
I guess this is one of the things where god went wrong, but I was just too lazy to write to the standards committee.
There should have been a realloc for array allocations:
p = renew(p) [128];
or something like that.
由于 C++ 面向对象的本质,以及包含各种标准容器类型,我认为与 C 相比,对方向内存管理的关注较少。我同意在某些情况下 realloc() 会很有用,但解决这个问题的压力很小,因为几乎所有由此产生的功能都可以通过使用容器来获得。
Because of the object oriented nature of C++, and the inclusion of the various standard container types, I think it's simply that less focus was placed on direction memory management than in C. I agree that there are cases that a realloc() would be useful, but the pressure to remedy this is minimal, as almost all of the resulting functionality can be gained by using containers instead.