ChatGPT解决这个技术问题 Extra ChatGPT

C++ 中的 std::vector 与 std::array

C++ 中的 std::vectorstd::array 有什么区别?什么时候应该优先于另一个?各自的优缺点是什么?我的教科书所做的就是列出它们的相同之处。

我正在寻找 std::vectorstd::array 的比较以及这些术语的不同之处。
Zud, std::array 与 C++ 数组不同。 std::array 是 C++ 数组的一个非常薄的包装器,主要目的是对类的用户隐藏指针。我会更新我的答案。
我更新了问题标题和文本以反映您的说明。
如果你正在实现 constexpr 或 consteval 函数,那么你可以使用 std::array,但不能使用 std::vector stackoverflow.com/questions/33241909/…

M
Matteo Italia

std::vector 是一个模板类,它封装了一个动态数组1,存储在堆中,如果添加或删除元素,它会自动增长和缩小。它提供了所有钩子(begin()end()、迭代器等),使其能够与 STL 的其余部分一起正常工作。它还有几个有用的方法可以让您执行在普通数组上会很麻烦的操作,例如在向量中间插入元素(它处理在幕后移动以下元素的所有工作)。

由于它将元素存储在堆上分配的内存中,因此相对于静态数组有一些开销。

std::array 是一个模板类,它封装了一个静态大小的数组,存储在对象本身中,这意味着,如果您在堆栈上实例化该类,则数组本身将在堆栈上。它的大小必须在编译时知道(它作为模板参数传递),它不能增长或缩小。

它比 std::vector 更受限制,但通常更有效,特别是对于小尺寸,因为实际上它主要是 C 样式数组的轻量级包装器。但是,它更安全,因为隐式转换为指针被禁用,并且它提供了 std::vector 和其他容器的许多与 STL 相关的功能,因此您可以轻松地将它与 STL 算法和公司无论如何,由于固定大小的限制,它远不如 std::vector 灵活。

有关 std::array 的介绍,请查看 this article;要快速介绍 std::vector 及其可能的操作,您可能需要查看它的 documentation

实际上,我认为在标准中,它们是根据不同操作的最大复杂度来描述的(例如,在恒定时间内随机访问,在线性时间内迭代所有元素,在恒定摊销时间内添加和删除元素,等),但是AFAIK除了使用动态数组之外没有其他方法可以满足此类要求。正如@Lucretiel 所述,该标准实际上要求元素连续存储,因此它是一个动态数组,存储在相关分配器放置的位置。


关于您的脚注:虽然正确,但该标准还保证内部元素上的指针算术有效,这意味着它必须是一个数组: &vec[9] - &vec[3] == 6 是正确的。
我很确定,该向量不会自动收缩,但从 C++11 开始,您可以调用 shrink_to_fit。
我完全被 static array 这个术语弄糊涂了,我不确定正确的术语是什么。您的意思是静态大小数组而不是静态变量数组(使用静态存储的数组)。 stackoverflow.com/questions/2672085/…。什么是正确的术语?对于具有固定大小的数组,静态数组是一个草率的术语吗?
@Zboson:绝对不只是你, static 是一个相当滥用的术语; C++ 中的very static 关键字具有三种不同的不相关含义,并且该术语还经常用于谈论在编译时固定的东西。我希望“静态大小”更清楚一点。
需要注意的一件事:对于实时编程(在启动后您不应该进行任何动态分配/解除分配),std::array 可能比 std::vector 更受欢迎。
M
Mark Lakata

为了强调@MatteoItalia 的观点,效率差异在于数据的存储位置。堆内存(vector 需要)需要调用系统来分配内存,如果您计算周期,这可能会很昂贵。堆栈内存(可能用于 array)在时间方面实际上是“零开销”,因为内存是通过调整堆栈指针来分配的,并且只在进入函数时完成一次。堆栈还避免了内存碎片。可以肯定的是,std::array 并不总是在堆栈中;这取决于您分配它的位置,但与向量相比,它仍然会减少从堆中分配的内存。如果你有一个

小“数组”(例如 100 个元素以下)-(典型的堆栈约为 8MB,因此如果您的代码是递归的,则不要在堆栈上分配超过几 KB 或更少)

大小将固定

生命周期在函数范围内(或者是与父类具有相同生命周期的成员值)

你在计算周期,

绝对在向量上使用 std::array。如果这些要求中的任何一个不正确,则使用 std::vector


不错的答案。 “可以肯定的是,std::array 并不总是在堆栈上;这取决于你在哪里分配它”那么我怎么能创建一个不在堆栈上的 std::array 有大量元素呢?
@Trilarion 使用 new std::array 或使其成为您使用“新”分配的类的成员。
所以这意味着 new std::array 仍然希望在编译时知道它的大小并且不能改变它的大小但仍然存在于堆上?
是的。使用 new std::arraynew std::vector 并没有显着优势。
C
ClosureCowboy

使用 std::vector<T> 类:

...与使用内置数组一样快,假设您只做内置数组允许您做的事情(读取和写入现有元素)。

...插入新元素时自动调整大小。

...允许您在向量的开头或中间插入新元素,自动“向上”“移动”其余元素(这有意义吗?)。它还允许您删除 std::vector 中任何位置的元素,自动将其余元素向下移动。

...允许您使用 at() 方法执行范围检查读取(如果您不希望执行此检查,您始终可以使用索引器 [])。

使用 std::vector<T> 两个 三个主要注意事项:

您无法可靠地访问底层指针,如果您正在处理需要数组地址的第三方函数,这可能是一个问题。 std::vector 类很愚蠢。它被实现为一个压缩位域,而不是一个数组。如果您想要一个布尔数组,请避免使用它!在使用期间,std::vectors 将比具有相同元素数量的 C++ 数组大一点。这是因为它们需要跟踪少量其他信息,例如它们的当前大小,并且因为每当 std::vector 调整大小时,它们会保留比它们需要的更多空间。这是为了防止它们在每次插入新元素时都必须调整大小。可以通过提供自定义分配器来更改此行为,但我从未觉得有必要这样做!

编辑:阅读 Zud 对问题的回复后,我觉得我应该添加以下内容:

std::array<T> 类与 C++ 数组不同。 std::array<T> 是 C++ 数组的一个非常薄的包装器,主要目的是对类的用户隐藏指针(在 C++ 中,数组被隐式转换为指针,通常会产生令人沮丧的效果)。 std::array<T> 类还存储其大小(长度),这非常有用。


它与使用动态分配的内置数组“一样快”。另一方面,使用自动数组可能具有相当不同的性能(不仅在分配期间,因为局部性影响)。
对于 C++11 及更高版本中的非布尔向量,您可以在 std::vector<T> 上调用 data() 以获取基础指针。您也可以只获取元素 0 的地址(保证与 C++11 一起使用,可能与早期版本一起使用)。
在最后一段中,您的意思是 C 数组?正确的 ?
p
psimpson

如果您正在考虑使用多维数组,那么 std::array 和 std::vector 之间还有一个区别。多维 std::array 将所有维度的元素都打包在内存中,就像 ac 样式数组一样。多维 std::vector 不会在所有维度中打包。

鉴于以下声明:

int cConc[3][5];
std::array<std::array<int, 5>, 3> aConc;
int **ptrConc;      // initialized to [3][5] via new and destructed via delete
std::vector<std::vector<int>> vConc;    // initialized to [3][5]

指向 c 样式数组 (cConc) 或 std::array (aConc) 中第一个元素的指针可以通过将每个前面的元素加 1 来遍历整个数组。它们包装得很紧。

指向向量数组 (vConc) 或指针数组 (ptrConc) 中第一个元素的指针只能遍历前 5 个(在这种情况下)元素,然后有 12 个字节(在我的系统上)的开销用于下一个向量。

这意味着初始化为 [3][1000] 数组的 std::vector> 数组在内存中将比初始化为 [1000][3] 数组的数组小得多,并且两者在内存中都将大于 std:数组分配任何一种方式。

这也意味着您不能简单地将多维向量(或指针)数组传递给例如 openGL 而不考虑内存开销,但您可以天真地将多维 std::array 传递给 openGL 并让它解决。


F
Frida Schenker

将上述讨论总结在一个表格中以供快速参考:

C 样式数组 std::array std::vector 大小 固定/静态 固定/静态 动态内存效率 更高效 更高效 效率较低(可能在新分配时将其大小翻倍。) 复制 迭代元素或使用 std::copy()直接复制:a2 = a1;直接复制:v2 = v1;传递给函数 通过指针传递。 (大小在函数中不可用) 按值传递 按值传递(在该函数中可用大小) 大小 sizeof(a1) / sizeof(a1[0]) a1.size() v1.size() 用例 用于快速访问和何时不经常需要的插入/删除。与经典数组相同,但更安全、更容易传递和复制。可能需要频繁添加或删除时


void foo(T (& arr)[N]) 将捕获数组大小。类似的magic-arguments-in-function-templates
我会添加这些行:“|值语义|否|是|是|”和“| 移动 | O(N) | O(N) | O(1) |”和“| 交换 | O(N) | O(N) | O(1) |”
S
Saif al Harthi

向量是容器类,而数组是分配的内存。


您的答案似乎针对 std::vector<T>T[],但问题是关于 std::vector<T>std::array<T>