为啥使用 std::vector 而不是 realloc? [关闭]

Posted

技术标签:

【中文标题】为啥使用 std::vector 而不是 realloc? [关闭]【英文标题】:Why use std::vector instead of realloc? [closed]为什么使用 std::vector 而不是 realloc? [关闭] 【发布时间】:2020-12-04 01:59:42 【问题描述】:

这里,在this question 中,声明在c++ 中没有类似realloc 的运算符或函数。如果您想调整数组大小,只需 std::vector 即可。我什至看到 Stroustrup 说过同样的话here。

我相信实施一个并不难。应该有不实施的理由。答案只说使用std::vector,但没有说为什么没有实现。

没有实现realloc-like 运算符或函数而是更喜欢使用std::vector 的原因是什么?

【问题讨论】:

vector 随时可用、功能齐全且经过良好测试。为什么要重新发明***? 这能回答你的问题吗? How do you 'realloc' in C++? “我相信实现一个并不难。” — 在重新分配期间,您将如何处理 non-trivially-copyable 类型?跨度> @StackExchange123 为什么要这样复制std::vector 的功能?请注意,这并不像您写的那么容易。您需要关心异常安全,类型可以具有复制/非抛出移动/抛出移动构造函数,您需要分别处理这些情况,等等。 @d4rk4ng31 -- 我不知道你所说的“微不足道”的内存块是什么意思。在这一切之下,它只是原始内存,realloc 有时可以扩展内存块,而newdelete 永远不能。有几次建议添加一个renew 运算符,它可以做同样的事情:如果他们的房间在适当的位置扩展一个内存块,然后初始化占用新添加内存的对象。 【参考方案1】:

好吧,因为其他答案已经很好地解释了使用向量的原因,我将简单地详细说明为什么 realloc 没有实现。为此,您需要查看realloc 的实际作用。它通过智能地使用malloc()free() 来增加内存的大小。你需要明白,虽然它看起来只是增加了大小,但实际上并没有增加大小,而是分配了另一个具有所需大小的内存块(这解释了名称realloc)。

看看下面几行:

int* iarr = (int*)malloc(sizeof(iarr)*5);
iarr = (int*)realloc(6,sizeof(iarr));  //this is completely discouraged
//what you're supposed to do here is:
int* iarr2 = (int*)realloc(iarr,1 + sizeof(iarr));  //copies the old data to new block implicitly
//this not only saves the previous state, but also allows you to check if realloc succeeded

在 C++ 中,这可以(如果必须)通过以下方式实现:

int* iarr = new int[5];
int* iarr2 = new int[6];
for(int i = 0; i < 5; i++) 
    iarr2[i] = iarr[i];

delete[] iarr;

realloc 的唯一用途是增加内存容量;由于C arrays 没有自动这样做,他们必须提供一种机制来这样做;这已在大多数 containers 中隐式实现,因此首先使用 realloc 的原因没有实际意义。

【讨论】:

我相信realloc 会尽可能扩展分配的内存。如果没有,则使用freemalloc @StackExchange123 但是如果它不能扩展,那么realloc() 将原始数据从旧分配复制到新分配,这仅适用于可简单复制的类型。例如。 realloc()std::string 的数组上是未定义的行为,除非分配可以在没有副本的情况下扩展。我猜std::vector 可以 SFINAE 在 std::is_trivial 上重新分配它,但是它需要使用 malloc()free() 而不是 C++ 分配器。 @cdhowie,另外,它与 new 和 delete 不兼容。我认为真正的问题是,why is realloc not compatible with new and delete? @StackExchange123,没那么容易 @StackExchange123 可能是因为vector的开销很小,而且它的效用远远超过了这个开销。 :) realloc 没有 C++ 标准的安全版本,因为人们宁愿使用向量也不愿使用类似向量的东西。【参考方案2】:

没有实现realloc-like 运算符或函数而是更喜欢使用std::vector 的原因是什么?

节省时间。不要在你自己的代码中追逐一个早已解决的问题。惯用的 C++ 和可读性。轻松快速地获得问题的答案。通过分配器自定义realloc 部分。

我相信实现一个并不难

这在很大程度上取决于您需要从您打算编写的模板中获得什么。对于通用的std::vector-like ,请查看源代码(libcxx 的 3400 行 vector 标头为 here)。我敢打赌你会修正你对这种结构的低复杂性的最初假设。

【讨论】:

【参考方案3】:

realloc 期望在当前分配之后可能有足够的可用空间,这与现代分配器和现代程序不太匹配。

(还有更多的分配正在进行,许多分配大小进入该大小的专用池,并且堆在程序中的所有线程之间共享。)

在大多数情况下,realloc 必须将内容移动到全新的分配中,就像 vector 一样。但与vector&lt;T&gt;不同,realloc不知道如何移动T类型的元素,它只知道如何复制纯数据。

【讨论】:

64 位系统有大量的虚拟地址空间,大的分配通常会从操作系统获得一些新的页面(例如通过mmap)。一些操作系统甚至有像 Linux 的 mremap(MREMAP_MAYMOVE) 这样的系统调用,它是一个页面粒度 realloc,即使在当前位置没有空间让这个映射增长,也可以避免复制。 (只需更新页表,以便将相同的物理页面映射到新的虚拟地址,最后有额外的空间)。不值得因为小尺寸而被 TLB 击落,但值得几 GiB。【参考方案4】:

有几个优点。

Vector 会跟踪其大小和容量,这意味着您不必自己执行此操作。 因为当前大小是矢量对象本身的一部分,所以您可以传递一个矢量(通过引用或通过值)而不需要额外的大小参数。这在返回向量时特别有用,因为调用者不需要通过某个边通道接收大小。 在重新分配时,向量将添加比仅添加请求添加的元素所需的容量更多的容量。这听起来很浪费,但可以节省时间,因为需要的重新分配更少。 Vector 管理自己的内存;使用向量可以让您专注于程序中更有趣的部分,而不是管理内存的细节,这些细节相对乏味且难以完全正确。 Vector 支持数组本身不支持的许多操作,例如从中间移除元素和制作整个向量的副本。

【讨论】:

以上是关于为啥使用 std::vector 而不是 realloc? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

为啥 'std::vector<int> b2;'创建一个 1 元素向量,而不是 2 元素向量?

为啥使用指针的 STL 算法比 std::vector 迭代器快得多?

为啥 std::vector 需要 is_trivial 进行按位移动,而不仅仅是 is_trivially_copyable?

std::vector 段错误而不是抛出异常

为啥 std::vector::data() 中没有使用指针 typedef?

为啥不能对 std::vector 使用前向声明?