一个不同的问题启发了以下想法:
当
据我所知,标准行为是底层分配器请求一个新大小的整个块,然后将所有旧元素移动过去,然后销毁旧元素,最后释放旧内存。
在标准分配器界面给出的情况下,这种行为似乎是唯一可能的正确解决方案。但是我想知道,修改分配器以提供一个
(
这似乎是一个被忽视的机会。最坏的情况下,您总是可以将
在C ++中,是否存在任何问题使此功能不合适或不可取?
也许唯一可以利用此功能的容器是
更新:一个小例子以澄清。当前的
当
std::vector<T>
增加其容量时,是否必须移动所有元素?据我所知,标准行为是底层分配器请求一个新大小的整个块,然后将所有旧元素移动过去,然后销毁旧元素,最后释放旧内存。
在标准分配器界面给出的情况下,这种行为似乎是唯一可能的正确解决方案。但是我想知道,修改分配器以提供一个
reallocate(std::size_t)
函数是否有意义,可以返回一个pair<pointer, bool>
并映射到底层realloc()
?这样做的优点是,如果操作系统实际上只能“扩展”已分配的内存,则根本不需要移动。布尔值将指示内存是否移动。(
std::realloc()
也许不是最好的选择,因为如果我们无法扩展,则无需复制数据。因此,事实上,我们更想要像extend_or_malloc_new()
这样的东西。 编辑:也许基于is_pod
的特化可以允许我们使用实际的realloc
,包括其位拷贝。只是不是普遍适用的。)这似乎是一个被忽视的机会。最坏的情况下,您总是可以将
reallocate(size_t n)
实现为 return make_pair(allocate(n), true);
,因此不会有任何惩罚。在C ++中,是否存在任何问题使此功能不合适或不可取?
也许唯一可以利用此功能的容器是
std::vector
,但是那又是一个非常有用的容器。
更新:一个小例子以澄清。当前的
resize()
:pointer p = alloc.allocate(new_size);
for (size_t i = 0; i != old_size; ++i)
{
alloc.construct(p + i, T(std::move(buf[i])))
alloc.destroy(buf[i]);
}
for (size_t i = old_size; i < new_size; ++i)
{
alloc.construct(p + i, T());
}
alloc.deallocate(buf);
buf = p;
新实现:
pair<pointer, bool> pp = alloc.reallocate(buf, new_size);
if (pp.second) { /* as before */ }
else { /* only construct new elements */ }
realloc
指针的时候,损坏已经发生了。 - David Rodríguez - dribeasstd::deque
是最不幸的容器之一。它在自己擅长的方面表现得非常出色,但你几乎从不需要它所擅长的功能。相较于std::deque
, 一个几何级数增长的循环缓冲区将是一个更好的 std:: 容器候选。循环缓冲区具有更好的性能和更少的复杂性。但是它不能像std::deque
和std::list
那样保证引用的稳定性。根据我的经验,循环缓冲区比std::deque
更好地解决了大多数推入/弹出队列问题。当循环缓冲区无法解决问题时,std::list
是正确的选择。 - Howard Hinnantoperator new
。相反,他建议vector
实现可以专门化其分配器以使用realloc
来移动平凡可复制对象(可能在原地扩展分配而不移动),或者使用calloc
来初始化零初始化对象(尽管似乎没有一种特性可以检测到类是否具有等效于零初始化构造函数)。 - BeeOnRope