【问题标题】:how c++ vector worksc++向量是如何工作的
【发布时间】:2012-06-16 22:55:33
【问题描述】:

假设我有一个向量 V,它有 10 个元素。 如果我使用v.erase(v.begin()) 擦除第一个元素(在索引 0 处),那么 STL 向量如何处理这个?

它是否会创建另一个新向量并将元素从旧向量复制到新向量并释放旧向量?或者它是否从索引 1 开始复制每个元素并将元素复制到 index-1 ?

如果我一次需要一个大小为 100,000 的向量,然后我不使用那么多空间,假设我只需要一个大小为 10 的向量,那么它会自动减小大小吗? (我不这么认为)

我在网上看了,只有 API 和教程如何使用 STL 库。 有什么好的参考资料可以让我了解 STL 库的实现或复杂性吗?

【问题讨论】:

标签: c++ stl


【解决方案1】:

Vector 会将元素复制(在 C++11 中移动)到开头,这就是为什么如果您想从集合的开头插入和擦除,您应该使用 deque。如果您想真正调整向量的内部缓冲区大小,可以这样做:

vector<Type>(v).swap(v);

这有望创建一个大小正确的临时向量,然后将其内部缓冲区与旧向量交换,然后临时向量超出范围,大缓冲区被释放。

正如其他人所说,您可以在 C++11 中使用 vector::shrink_to_fit()

【讨论】:

    【解决方案2】:

    我只需要一个大小为 10 的向量,那么它会自动减小大小吗?

    不,它不会自动缩小。
    传统上,你用一个新的空向量交换向量:reduce the capacity of an stl vector

    但是 C++x11 包含一个 std::vector::shrink_to_fit() ,它直接执行它

    【讨论】:

    • @MooingDuck - 其他帖子已经回答了这些问题 - 这只是解决一个问题
    【解决方案3】:

    它是否从索引 1 开始复制每个元素并将元素复制到索引 1 ?

    是的(尽管它实际上从 C++11 开始移动它们)。

    它会自动缩小尺寸吗?

    不,减小大小通常会使现有元素的迭代器无效,并且只允许在某些函数调用中。

    我在网上看了,只有 API 和教程如何使用 STL 库。有没有什么好的参考资料可以让我了解 STL 库的实现或复杂性?

    您可以阅读 C++ 规范,该规范会准确地告诉您在实现方面什么是允许的,什么是不允许的。你也可以去看看你的实际实现。

    【讨论】:

      【解决方案4】:

      这是我(很多)反对 C++ 的原因之一。每个人都说“使用标准库”......但即使你有 STL 源代码(可以从许多不同的地方免费获得。在这种情况下,包括头文件本身!)......它基本上是一个难以理解的噩梦深入了解并尝试理解。

      相比之下,(仅 C 语言)Linux 内核是简洁明了的典范。

      但我们离题了:)

      这是您问题的 10,000 英尺答案:

      http://www.cplusplus.com/reference/stl/vector/

      向量容器被实现为动态数组;和平常一样 数组,向量容器的元素存储在连续的 存储位置,这意味着它们的元素不能被访问 只使用迭代器,但也在常规指针上使用偏移量 元素。

      但与常规数组不同的是,向量中的存储是经过处理的 自动,允许它根据需要扩展和收缩。

      向量擅长:

      • 通过位置索引(恒定时间)访问各个元素。
      • 以任何顺序(线性时间)迭代元素。
      • 从其末尾添加和删除元素(恒定摊销时间)。

      与数组相比,它们为这些提供几乎相同的性能 任务,此外,它们还可以轻松调整大小。虽然,他们 处理容量时通常比数组消耗更多的内存 自动(这是为了容纳额外的存储空间 未来的增长)。

      与其他基本标准序列容器(deques 和 列表),向量通常是最有效的访问时间 元素并从序列末尾添加或删除元素。 对于涉及插入或删除元素的操作 除了末尾以外的位置,它们的性能比双端队列和 列表,并且迭代器和引用的一致性不如列表。

      ...

      就性能而言,重新分配可能是一项代价高昂的操作,因为 它们通常涉及向量使用的整个存储空间 被复制到新位置。您可以使用成员函数 vector::reserve 预先指示向量的容量。这 可以帮助优化存储空间并减少重新分配的次数 当计划进行许多扩建时。

      ...

      【讨论】:

      • 如果你有一个好的参考,这不是不可理解的。每个容器都有其优点和缺点,并且像任何一组工具一样,在应用于问题之前需要了解。
      【解决方案5】:

      其实vector的实现是可见的,因为它是一个模板,所以你可以查看它的详细信息:

      iterator erase(const_iterator _Where)
          {   // erase element at where
          if (_Where._Mycont != this
              || _Where._Myptr < _Myfirst || _Mylast <= _Where._Myptr)
              _DEBUG_ERROR("vector erase iterator outside range");
          _STDEXT unchecked_copy(_Where._Myptr + 1, _Mylast, _Where._Myptr);
          _Destroy(_Mylast - 1, _Mylast);
          _Orphan_range(_Where._Myptr, _Mylast);
          --_Mylast;
          return (iterator(_Where._Myptr, this));
          }
      

      基本上就是一行

      unchecked_copy(_Where._Myptr + 1, _Mylast, _Where._Myptr);
      

      完全按照您的想法进行 - 复制以下元素(或如 bames53 指出的那样在 C++11 中移动它们)。

      回答你的第二个问题,不,容量不能自行减少。

      std 中算法的复杂性可以在http://www.cplusplus.com/reference/stl/ 中找到,并且如前所述,实现是可见的。

      【讨论】:

      • 我从没想过我会看到你引用那个网站。好的也有复杂性。
      • 学究式:vector一个实现是可见的。有不止一种实现。
      • @GManNickG 并且要在你的迂腐中迂腐 - 所有 实现都是可见的,但它们不一定与这个相同。 :) 我想这就是你的意思。
      • @LuchianGrigore:很公平。为了提高学究气,所有当前实现发生都是可见的;不一定有任何用 C++ 代码编写的 vector 实现。 :)
      猜你喜欢
      • 2020-06-01
      • 1970-01-01
      • 2011-11-23
      • 2018-09-13
      • 2012-07-30
      • 2010-10-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多