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

c++中std::vectorstd::array的区别是什么?什么时候应该优先选择一个?它们各自的优点和缺点是什么?我的课本只列出了它们的相同之处。

163307 次浏览

vector是容器类,而array是已分配的内存。

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

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

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

它比std::vector更有限制,但它通常更有效,特别是对于较小的大小,因为在实践中它主要是c风格数组的轻量级包装器。然而,它更安全,因为指针的隐式转换被禁用了,并且它提供了std::vector和其他容器的许多与STL相关的功能,所以你可以很容易地将它与STL算法一起使用。无论如何,由于固定大小的限制,它比std::vector灵活得多。

有关std::array的介绍,请查看这篇文章;要快速介绍std::vector及其上可能的操作,你可能想要查看它的文档


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

使用std::vector<T>类:

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

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

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

  • ...允许你使用at()方法执行范围检查读取(如果你不希望执行这个检查,你总是可以使用索引器[])。

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

  1. 您没有对底层指针的可靠访问,如果您正在处理需要数组地址的第三方函数,则可能是一个问题。

  2. std::vector<bool>类很愚蠢。它被实现为一个压缩的位域,而不是数组。如果你想要一个__abc1数组,请避免使用它!

  3. 在使用过程中,__abc0将比具有相同数量元素的c++数组略大。这是因为它们需要跟踪少量的其他信息,例如它们的当前大小,并且因为每当__abc0调整大小时,它们会保留比所需的更多空间。这是为了防止每次插入新元素时都必须调整大小。这种行为可以通过提供自定义allocator来改变,但我从未觉得有必要这样做!


编辑:在阅读了Zud对这个问题的回复后,我觉得我应该加上这个:

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

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

  • 小的“数组”(少于100个元素)-(一个典型的堆栈大约是8MB,所以不要在堆栈上分配超过几KB的空间,如果你的代码是递归的)
  • 尺寸是固定的
  • 生命期在函数作用域中(或者是与父类具有相同生命期的成员值)
  • 你在计算周期,

在vector上使用std::array。如果这些要求中的任何一个不是真的,那么使用std::vector

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

给定以下声明:

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:array大。

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

将上述讨论总结为表格,以供快速参考:

c风格的数组 std::数组 std::向量
大小 固定/静态 固定/静态 动态
记忆效率 更高效的 更高效的 更低的效率
(可能在新分配时翻倍它的大小。)
复制 遍历
的元素或使用std::copy()
直接复制:a2 = a1; 直接复制:v2 = v1;
传递给函数 由指针传递。
(大小在函数中不可用)
通过值传递 通过value
(该函数中可用的大小)传递
大小 Sizeof (a1) / Sizeof (a1[0]) a1.size () v1.size ()
用例 用于快速访问和
时不经常需要插入/删除。
与经典数组相同,但
更安全,更容易传递和复制。
当需要频繁添加或删除