【问题标题】:std::vector versus std::array in C++C++ 中的 std::vector 与 std::array
【发布时间】:2011-05-24 09:36:01
【问题描述】:

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

【问题讨论】:

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

标签: c++ arrays vector


【解决方案1】:

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

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

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

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

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


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

【讨论】:

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

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

  • 小型“数组”(比如 100 个元素以下)-(典型堆栈约为 8MB,因此如果您的代码是递归的,则不要在堆栈上分配超过几 KB 或更少)
  • 大小将固定
  • 生命周期在函数范围内(或者是与父类具有相同生命周期的成员值)
  • 您正在计算周期,

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

【讨论】:

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

使用std::vector<T> 类:

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

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

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

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

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

  1. 您无法可靠地访问底层指针,如果您正在处理需要数组地址的第三方函数,这可能会成为一个问题。

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

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


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

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

【讨论】:

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

如果您正在考虑使用多维数组,那么 std::array 和 std::vector 之间还有一个区别。多维 std::array 将所有维度的元素都打包在内存中,就像 c 样式数组一样。多维 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:array。

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

【讨论】:

    【解决方案5】:

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

    C-Style Array std::array std::vector
    Size Fixed/Static Fixed/Static Dynamic
    Memory efficiency More efficient More Efficient Less efficient
    (May double its size on new allocation.)
    Copying Iterate over elements
    or use std::copy()
    Direct copy: a2 = a1; Direct copy: v2 = v1;
    Passing to function Passed by pointer.
    (Size not available in function)
    Passed by value Passed by value
    (Size available in that function)
    Size sizeof(a1) / sizeof(a1[0]) a1.size() v1.size()
    Use case For quick access and when
    insertions/deletions not frequently needed.
    Same as classic array but
    safer and easier to pass and copy.
    When frequent additions or
    deletions might be needed

    【讨论】:

    • void foo(T (&amp; arr)[N]) 将捕获数组大小。类似magic-arguments-in-function-templates
    • 我会添加这些行:“| 值语义 | no | yes | yes |”和“| 移动 | O(N) | O(N) | O(1) |”和“| 交换 | O(N) | O(N) | O(1) |”
    【解决方案6】:

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

    【讨论】:

    • 您的答案似乎针对std::vector&lt;T&gt;T[],但问题是关于std::vector&lt;T&gt;std::array&lt;T&gt;
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-15
    相关资源
    最近更新 更多