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


283

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


1
我正在寻找std::vectorvs. std::array以及术语之间的差异。
Zud 2010年

Zud,std::array与C ++数组不同。std::array是围绕C ++数组的非常薄的包装器,其主要目的是向类用户隐藏指针。我将更新我的答案。
ClosureCowboy

我更新了问题标题和文字以反映您的澄清。
Matteo Italia 2010年

Answers:


318

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

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

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

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

作为介绍std::array,看一下这篇文章 ; 有关其std::vector可能的操作的快速介绍和操作,您可能需要查看其文档


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

6
关于您的脚注:虽然为true,但该标准还保证内部元素上的指针算术有效,这意味着它必须是数组:&vec [9]-&vec [3] == 6为true。
Lucretiel

9
我很确定,向量不会自动收缩,但是从C ++ 11开始,您可以调用rinkle_to_fit。
迪诺2014年

我对“ 静态数组 ”一词完全感到困惑,我不确定什么是正确的术语。您的意思是静态大小数组,而不是静态变量数组(使用静态存储的数组)。stackoverflow.com/questions/2672085/…。正确的术语是什么?静态数组是固定大小数组的草率术语吗?
Z玻色子2014年

3
@Zboson:绝对不仅是您,static是一个非常滥用的名词。staticC ++中的very 关键字具有三种不相关的含义,该术语还经常用于谈论在编译时固定的内容。我希望“静态大小”更清晰一些。
Matteo Italia

3
需要注意的一件事:对于实时编程(启动后不应进行任何动态分配/释放),std :: array可能比std :: vector更受青睐。
TED

17

使用std::vector<T>类:

  • …… 就像使用内置数组一样快,假设您仅在执行内置数组允许的操作(对现有元素进行读写)。

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

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

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

2点使用三种主要的注意事项std::vector<T>

  1. 您没有对基础指针的可靠访问,如果要处理需要数组地址的第三方函数,则可能会遇到问题。

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

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


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

std::array<T>类是不一样的,为C ++阵列。std::array<T>是围绕C ++数组的非常薄的包装器,其主要目的是向类用户隐藏指针(在C ++中,数组隐式转换为指针,通常会产生令人沮丧的效果)。所述std::array<T>类还存储它的大小(长度),其可以是非常有用的。


5
它是“一样快”为使用动态分配的内置阵列。另一方面,使用自动数组可能有很大的不同表现(而不是只分配时,由于局部性影响)。
本·福格特

4
对于C ++ 11和更高版本中的非布尔向量,可以调用data()a std::vector<T>获取基础指针。您也可以只获取元素0的地址(保证与C ++ 11一起使用,可能会与早期版本一起使用)。
马特

16

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

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

绝对使用std::array一个向量。如果这些要求中的任何一项都不成立,请使用std::vector


3
好答案。“可以肯定的是,std :: array并不总是在堆栈上;这取决于您分配它的位置。”那么,如何在不具有大量元素的堆栈上创建std :: array呢?
Trilarion

4
@Trilarion使用new std::array或使其成为您使用'new`进行分配的类的成员。
Mark Lakata '17

因此,这意味着new std::array仍然希望在编译时知道其大小,并且无法更改其大小,但仍驻留在堆中吗?
Trilarion

是。使用new std::arrayvs 没有明显的优势new std::vector
Mark Lakata '17

12

如果您正在考虑使用多维数组,则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 :: vector>数组小得多,并且两者在内存中都将比std大:数组以任何一种方式分配。

这也意味着您不能在不考虑内存开销的情况下简单地将多维矢量(或指针)数组传递给openGL,但是您可以将一个多维std :: array天真地传递给openGL并使其工作。


-17

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


23
你的回答似乎地址std::vector<T>T[],但问题是关于std::vector<T>std::array<T>
Keith Pinson
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.