是公平的总是推荐std :: vector over realloc吗? [英] Is it fair to always recommend std::vector over realloc?

查看:258
本文介绍了是公平的总是推荐std :: vector over realloc吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Bjarne Stroustrup的常见问题


如果你觉得需要realloc() - 和许多,那么考虑使用
a标准库向量。


我会先同意我的问题,同意 std :: vector 是更好的许多原因,我个人会总是选择使用它



std :: vector realloc 编辑)的等效内容。要澄清,我知道 std: :vector 的存储是连续的并且不会被分段,我的意思是分配和释放引起的内存空间的碎片, realloc 可以通过扩展现有分配来避免)。那么公平地总是推荐它 realloc ?非常小心,你不能写一些工作就像 std :: vector 但是使用C分配函数,它有可能增长其内存,而不移动其地址和复制现有元素,使其在分段和性能方面更好或更好?



相关(奖金问题!), t C ++等价于 realloc ?看起来像一个奇怪的事情,忽略在一个如此专注于性能的语言。在Bjarne的FAQ中的部分具有正确的标题(减去重点),但答案不解决为什么。这只是一个意外的遗漏吗?是否与 new / delete 如何工作有一些根本不兼容?



编辑:确定,所以我忽略了考虑C的妄想。 code> realloc - std :: vector 不能使用 >因为它只适用于POD,不会抛出等等。也许一个只有POD的容器是为了处理这种谬误,在某些情况下是一个好主意。在任何情况下,更有趣的问题变成:< std :: vector 受益于 realloc (或多或少)已在这里回答:



std :: vector *在增加容量时是否移动对象?或者,可以分配器重新分配?



很遗憾,答案似乎是是的,但标准委员会没有投票。这是希望。

解决方案

直接比较



  | std :: vector | C内存函数
------------------------ + ------------------ + ------------------------
默认容量|未定义| undefined
default grow |对能力| undefined
确定性容量|可用|没有
确定性增长可用| no
deterministic mem.-move |可用| no
非POD类型|是| f *** ing no(*)
no-throw | no | yes

确定性mem-移动 确定性容量/ grow 。当 realloc std :: vector 必须将存储的元素移动到新的内存位置。



我认为关于内存移动的(可用的)决定论在考虑移动(智能)任何类型的引用时是非常重要的。



注意:在这方面,我使用关于我的源代码生命周期的术语确定性,即它在具有不同编译标志的不同版本的不同版本之间的生命周期等。 / p>






c $ c> realloc 会执行:


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




向量的元素被连续存储,意味着如果 v T,Allocator> 其中 T 是除 bool 之外的某种类型, < code>& v [n] ==& v [0] + n v.size()


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

一个很大的区别是 realloc 实际上可以增加已分配的内存部分,而无需对其进行排序,不需要这样做( man 3 realloc ):


/ h3>

realloc()函数将ptr指向的内存块的大小更改为size字节。内容将
在从区域的开始到旧的和新的大小的最小值的范围内不变。如果新的
大小大于旧大小,则添加的内存将不会被初始化。如果ptr为NULL,那么对于size的所有值,调用
相当于malloc(size);如果size等于零,并且ptr不为NULL,那么调用
相当于free(ptr)。除非ptr为NULL,否则它必须早先调用malloc(),cal-
loc()或realloc()返回。 如果指向的区域已移动,则会执行一个免费(ptr)。




std :: vector 不仅包含 size ,还有一个 capacity 。如果你事先知道,你将需要一个大的向量,但是你不能初始化一切现在,你有权增加你的矢量的容量,如:

  std :: vector< T> vec(32); 
vec.reserve(1024);
// vec的大小为32,但保留了1024个元素的内存区域

不像 realloc ,重新分配发生的时刻可以是 std :: vector



要回答你的问题:因为有 std :: vector realloc 需要。并且, realloc 不允许用于非POD类型;尝试直接使用 malloc free realloc -PODs产生未定义的行为。


From Bjarne Stroustrup's FAQ:

If you feel the need for realloc() - and many do - then consider using a standard library vector.

I'll preface my question by agreeing that std::vector is better for many reasons, and I personally would always choose to use it over writing my own dynamic arrays with C memory allocation.

But, std::vector fragments memory as it grows because C++ has no equivalent of realloc (edit To clarify, I know that std::vector's storage is contiguous and won't get fragmented, I mean fragmentation of the memory space caused by allocating and deallocating, which realloc can avoid by extending an existing allocation). So is it fair to always recommend it over realloc? With great care, couldn't you write something that works just like std::vector but using C allocation functions, which has the possibility to grow its memory without moving its address and copying existing elements, making it as good or better in terms of fragmentation and performance?

And relatedly (bonus question!), why doesn't C++ have an equivalent to realloc? It seems like an odd thing to omit in a language that is so focused on performance. The section in Bjarne's FAQ has exactly that title (minus emphasis), but the answer doesn't address the 'why'. Was it just an accidental omission? Is there some fundamental incompatibility with how new/delete work? Does it not really give the benefits it seems to in practice?

Edit: ok, so I had neglected to consider the C nastiness of realloc - std::vector can't be rewritten using realloc because it only works with PODs, doesn't throw and so on. Perhaps a POD-only container written to deal with the nastiness would be a good idea for some situations. In any case though, the more interesting question becomes: would std::vector benefit from a C++ equivalent of realloc, which has (more or less) been answered here:

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

Sadly, the answer seems to be "yes, but the standards committee didn't vote it in". Here's hoping.

解决方案

Direct comparison

                        |  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 follows from deterministic capacity/grow. It is when realloc and std::vector have to move their stored elements to a new memory location.

I think the (available) determinism with regards to memory moving is doubly important when you consider moving (smart) references of any kind.

NOTE: In this respect, I use the term "deterministic" with respect to my source codes lifetime, i.e. its lifetime across different versions of different libraries with different compile flags, etc..


Source

It does fragment memory as much as realloc does:

Class template vector overview [vector.overview]

The elements of a vector are stored contiguously, meaning that if v is a vector<T, Allocator> where T is some type other than bool, then it obeys the identity &v[n] == &v[0] + n for all 0 <= n < v.size()

In other words, the memory used is in one piece.

The one big difference is that realloc can actually increase allocated memory portions without having ordered it to do so, however, it is not required to do so (man 3 realloc):

man 3 realloc

The realloc() function changes the size of the memory block pointed to by ptr to size bytes. The contents will be unchanged in the range from the start of the region up to the minimum of the old and new sizes. If the new size is larger than the old size, the added memory will not be initialized. If ptr is NULL, then the call is equivalent to malloc(size), for all values of size; if size is equal to zero, and ptr is not NULL, then the call is equivalent to free(ptr). Unless ptr is NULL, it must have been returned by an earlier call to malloc(), cal‐ loc() or realloc(). If the area pointed to was moved, a free(ptr) is done.

So it can increase the size, but is not required to.

A std::vector carries not only a size, but also a capacity. If you know beforehand you will need a big vector, yet you cannot initialize everything right now, you are entitled to increase the capacity of your vector like so:

std::vector<T> vec(32);
vec.reserve(1024);
// vec has size 32, but reserved a memory region of 1024 elements

So, unlike realloc, the moment when reallocations occur can be deterministic with std::vector.

To answer your question: Because there is std::vector, realloc is not needed. And, realloc is not allowed for non-POD types; attempts to use malloc, free and realloc directly on non-PODs yields undefined behaviour.

这篇关于是公平的总是推荐std :: vector over realloc吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆