首页 文章

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

提问于
浏览
248

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

6 回答

  • 277

    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 .


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

    使用 std::vector<T> 类:

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

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

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

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

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

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

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

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


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

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

  • 14

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

    • small "array"(在100个元素之下) - (典型的堆栈大约是8MB,所以如果你的代码是递归的,不要在堆栈上分配超过几KB或更少)

    • 大小将被修复

    • 生命周期在函数范围内(或者是与父类具有相同生命周期的成员值)

    • 你在计算周期,

    肯定在矢量上使用 std::array . 如果这些要求中的任何一个不成立,则使用 std::vector .

  • 15

    如果您正在考虑使用多维数组,那么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更大:数组分配方式 .

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

  • -15

    向量对数组的优势之一是可以使用 vector_name.size() 找到向量的 current size .

    可以想象,这在各种情况下非常有用,您可以轻松地获取array_list中的元素数量 .

  • 9

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

相关问题