C++ 中的 std::vector
和 std::array
有什么区别?什么时候应该优先于另一个?各自的优缺点是什么?我的教科书所做的就是列出它们的相同之处。
std::vector
与 std::array
的比较以及这些术语的不同之处。
std::array
与 C++ 数组不同。 std::array
是 C++ 数组的一个非常薄的包装器,主要目的是对类的用户隐藏指针。我会更新我的答案。
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 所述,该标准实际上要求元素连续存储,因此它是一个动态数组,存储在相关分配器放置的位置。
为了强调@MatteoItalia 的观点,效率差异在于数据的存储位置。堆内存(vector
需要)需要调用系统来分配内存,如果您计算周期,这可能会很昂贵。堆栈内存(可能用于 array
)在时间方面实际上是“零开销”,因为内存是通过调整堆栈指针来分配的,并且只在进入函数时完成一次。堆栈还避免了内存碎片。可以肯定的是,std::array
并不总是在堆栈中;这取决于您分配它的位置,但与向量相比,它仍然会减少从堆中分配的内存。如果你有一个
小“数组”(例如 100 个元素以下)-(典型的堆栈约为 8MB,因此如果您的代码是递归的,则不要在堆栈上分配超过几 KB 或更少)
大小将固定
生命周期在函数范围内(或者是与父类具有相同生命周期的成员值)
你在计算周期,
绝对在向量上使用 std::array
。如果这些要求中的任何一个不正确,则使用 std::vector
。
new std::array
或使其成为您使用“新”分配的类的成员。
new std::array
仍然希望在编译时知道它的大小并且不能改变它的大小但仍然存在于堆上?
new std::array
与 new std::vector
并没有显着优势。
使用 std::vector<T>
类:
...与使用内置数组一样快,假设您只做内置数组允许您做的事情(读取和写入现有元素)。
...插入新元素时自动调整大小。
...允许您在向量的开头或中间插入新元素,自动“向上”“移动”其余元素(这有意义吗?)。它还允许您删除 std::vector 中任何位置的元素,自动将其余元素向下移动。
...允许您使用 at() 方法执行范围检查读取(如果您不希望执行此检查,您始终可以使用索引器 [])。
使用 std::vector<T>
有 两个 三个主要注意事项:
您无法可靠地访问底层指针,如果您正在处理需要数组地址的第三方函数,这可能是一个问题。 std::vector
编辑:阅读 Zud 对问题的回复后,我觉得我应该添加以下内容:
std::array<T>
类与 C++ 数组不同。 std::array<T>
是 C++ 数组的一个非常薄的包装器,主要目的是对类的用户隐藏指针(在 C++ 中,数组被隐式转换为指针,通常会产生令人沮丧的效果)。 std::array<T>
类还存储其大小(长度),这非常有用。
std::vector<T>
上调用 data()
以获取基础指针。您也可以只获取元素 0 的地址(保证与 C++11 一起使用,可能与早期版本一起使用)。
如果您正在考虑使用多维数组,那么 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 并让它解决。
将上述讨论总结在一个表格中以供快速参考:
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
向量是容器类,而数组是分配的内存。
std::vector<T>
与 T[]
,但问题是关于 std::vector<T>
与 std::array<T>
。
static
关键字具有三种不同的不相关含义,并且该术语还经常用于谈论在编译时固定的东西。我希望“静态大小”更清楚一点。