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

Zud*_*Zud 269 c++ arrays vector

C++中的a std::vector和a有什么区别std::array?什么时候应该优先于另一个?各自的优点和缺点是什么?我的所有教科书都列出了它们是如何相同的.

Mat*_*lia 305

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所说,标准实际上要求元素连续存储,因此它是一个动态数组,存储在相关分配器放置的位置.

  • 我很确定,该向量不会自动缩小,但是从C++ 11开始,你可以调用shrink_to_fit. (9认同)
  • 关于你的脚注:虽然是真的,标准也保证内部元素上的指针算法有效,这意味着它必须是一个数组:&vec [9] - &vec [3] == 6是真的. (6认同)
  • @Zboson:这绝对不仅仅是你,*静态*是一个非常滥用的术语; C++中的`static`关键字有三种不同的无关含义,这个术语也经常被用来讨论在编译时修复的东西.我希望"静态大小"更清晰一些. (3认同)
  • 有一点需要注意:对于实时编程(在启动后你不应该****任何**动态分配/释放)std :: array可能比std :: vector更受欢迎. (3认同)

Clo*_*boy 15

使用std::vector<T>课程:

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

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

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

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

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

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

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

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


编辑:在阅读Zud对问题的回复后,我觉得我应该补充一下:

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

  • 它与使用动态分配的内置数组"一样快".另一方面,使用自动数组可能会有相当不同的性能(而且不仅在分配期间,因为地点效应). (4认同)
  • 对于C++ 11及更高版本中的非bool向量,可以在`std :: vector <T>`上调用`data()`来获取底层指针.您也可以只获取元素0的地址(保证与C++ 11一起使用,可能适用于早期版本). (3认同)

Mar*_*ata 14

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

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

绝对使用std::array一个矢量.如果这些要求中的任何一个不成立,那么使用a std::vector.

  • @Trilarion使用`new std :: array`或使其成为使用'new`分配的类的成员. (4认同)
  • 很好的答案."可以肯定的是,std :: array并不总是在堆栈上;它取决于你分配它的位置"那么我怎样才能在堆栈中创建一个std :: array并且包含大量元素? (3认同)
  • 所以这意味着 new std::array 仍然希望在编译时知道它的大小并且不能改变它的大小但仍然存在于堆上? (2认同)

psi*_*son 11

如果您正在考虑使用多维数组,那么std :: array和std :: vector之间还有一个区别.多维std :: array将在所有维度中将元素打包在内存中,就像交流样式数组一样.多维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]
Run Code Online (Sandbox Code Playgroud)

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

指向向量数组(vConc)或指针数组(ptrConc)中第一个元素的指针只能通过前5个(在本例中)元素迭代,然后有12个字节(在我的系统上)用于下一个矢量.

这意味着初始化为[3] [1000]数组的std :: vector>数组在内存中要小于作为[1000] [3]数组初始化的数组,并且内存中的数据都比std更大:数组分配方式.

这也意味着你不能简单地将一个多维向量(或指针)数组传递给openGL而不考虑内存开销,但是你可以天真地将多维std :: array传递给openGL并让它运行起来.


Fri*_*ker 5

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

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

可能需要频繁添加或删除时