【问题标题】:Why isn't a vector of a vector guaranteed to be contiguous? (or is it?)为什么向量的向量不能保证是连续的? (或者是吗?)
【发布时间】:2013-03-27 06:56:55
【问题描述】:

我知道std::vector 元素在内存中保证是连续的。

那么为什么你不能期望一个包含其他向量的向量的总集合是连续的呢?

该向量应该保证其封闭项的连续内存布局,如果这些附件也是向量,那么我希望最顶层向量的全部内容都在连续内存中。

但在这个问题上似乎存在一些关于这是否属实的争论。可以安全地依赖它吗?有些人似乎通过go out of their way 来实现这一点,而我认为这是有保证的。

【问题讨论】:

  • 向量不直接存储它们的数据。
  • 谢谢,但我不太明白你的意思。
  • 向量有一个指向其数据的指针。我想有可能制作一个可以做到这一点的分配器。
  • sizeof(vector) != vector.size()。只有向量持有的指针是连续的。
  • 如果对您有任何用处,我昨天提供了this contiguous solution 给正在考虑将向量向量用于二维矩阵的人。

标签: c++ stdvector


【解决方案1】:

向量向量中的每个向量都是一个单独的对象,因此负责它的存储。所以,不,它决不能保证是连续的,事实上我无法真正看到存储在外部向量和它的内部向量中的数据可能是一个连续的内存块的情况。

【讨论】:

  • 如果它真的想要,自定义分配器可以管理它。至于它是否以及为什么要这样做......
  • 是的,我想是这样,但您仍然会面临对齐问题或过度分配的风险。
  • 啊好的,我现在明白了,谢谢。不过我想像vector<float[3]>vector<std::array<float,3>> 这样的东西可以保证连续的内存,对吧?
  • 还要考虑std::swap(v[0], v[1]) 对向量的影响。那个交换不能改变v
【解决方案2】:

向量是一个包含指向实际数组的指针的对象。

向量的向量是一个带有指向对象数组的指针的对象,每个对象都指向堆上其他地方的自己的数组。所以不,它们永远不会按照您要求的方式连续。

【讨论】:

    【解决方案3】:

    我认为最正确的正式回答方式(与描述现有实现相反)是基于 §23.3.6.1/1:

    [...] 向量的元素是连续存储的,这意味着如果 vvector<T, Allocator> ,其中 T 是 bool 以外的其他类型,则它遵循恒等式

    &v[n] == &v[0] + n
    

    所有0 <= n < v.size()

    请注意,这里讨论的是向量各个元素的地址&v[i],特别是意味着向量的每个元素都具有恒定大小sizeof(T)(因为这是指针算法的工作原理)。

    这意味着向量的元素不可能在运行时改变大小。如果将vector<vector<T>> 实现为一个连续的内存块,则外部向量的成员(本身就是向量)将被允许更改大小。

    因此,必须有一个额外的间接级别,即各个向量必须包含某种指向存储在不同位置的可变大小数据结构的指针。

    【讨论】:

    • 谢谢。我已经意识到,我可以通过做vector<float[3]>vector<std::array<float,3>> 之类的事情来获得我想要的东西——其中任何一个都应该保证父向量中所有项目的内存连续性,从而保留“多维”——喜欢结构,同时享受完整的记忆连续性
    • @SebbyJohanns 当然。我的帖子的重点是表明您的问题(“否”)的答案直接来自标准,而不是通常如何实现向量。
    【解决方案4】:

    问题在于向量模板不包含它处理“内联”或直接处理的数据。另一种说法是向量类将其保存的数据数组装箱:向量类保存一个指向包含元素数组的内存区域的指针。它在结构上等价于:

    template <typename T>
    struct vector_eq {
        T *ptr;
    };
    

    而不是

    template <typename T>
    struct vector_neq {
        T arr[SIZE];
    };
    

    这需要在编译时知道SIZE(即constexpr),以便将arr 元素包含在结构中。

    我想应该可以专门化 vector&lt;vector&lt;T&gt;&gt; 以包含指向单个内存块的指针,并让它的方法返回 vector&lt;T&gt; 共享该内存块的片的临时实例,尽管它背后的逻辑可能会有点毛。

    【讨论】:

      【解决方案5】:

      我们看一下vector的(逻辑)内存布局:

      [size:4/8 bytes]
      [capacity:4/8 bytes]
      [other datamembers:n bytes]
      *[elements:size*sizeof(element) bytes] << one contiguous memory (pointer to heap)
      

      使用向量组成的向量看起来像这样:

      [size:4/8 bytes]
      [capacity:4/8 bytes]
      [other datamembers:n bytes]
      * [
          [Vector:0]
              [size:4/8 bytes]
              [capacity:4/8 bytes]
              [other datamembers:n bytes]
              *[elements:size*sizeof(element) bytes] << pointer to contiguous memory for elements
          [Vector:1]
              [size:4/8 bytes]
              [capacity:4/8 bytes]
              [other datamembers:n bytes]
              *[elements:size*sizeof(element) bytes]
          [Vector:2]
              [size:4/8 bytes]
              [capacity:4/8 bytes]
              [other datamembers:n bytes]
              *[elements:size*sizeof(element) bytes]
          ...
          ...
      ] << contiguous memory of vectors
      

      这意味着一个向量有一个指向连续内存存储向量的指针,每个向量存储它们的元素,指向另一个堆,其中连续内存存储元素。

      但是,如果您设法创建了一个供向量使用的分配器,以便分配连续的内存块,您仍然会遇到这样的问题,即如果嵌套向量之一被删除,则内存不可用更长的连​​续。更不用说嵌套向量大小不同的情况了。

      根据您的用例,您可以将客户的连续块内存分配器用于向量向量,也可以使用手动分配和释放一个连续内存块的旧方法。

      【讨论】:

      • 一个向量实际上包含一个指针,指向它之外的一块内存。这就是它可以调整自身大小的方式,您甚至可以拥有一个向量数组——结构本身是一个恒定大小。可以想象,一个连续的内存块包含所有元素,尽管如果不深入了解如何/何时分配内容,并且调整任何向量的大小可能会将其全部搞砸,这将是一件痛苦的事情。
      • @cHao:当你发表评论时,我实际上是在更新我的答案 :)
      【解决方案6】:

      简单地说 - std::vector 保证元素是连续存储的。但这仅包括元素的成员数据,在向量的情况下,它将是它的大小、容量和类似的东西,加上指向实际向量数据的指针。

      因此,向量向量将为您提供向量的连续向量,但这些向量的数据将动态分配在任意内存地址上。

      由于需要在编译时知道对象的大小,所以不能拥有大小不同的对象,唯一的方法是使用动态内存分配。如果您有一个固定大小的自定义向量,内部不使用动态内存分配,那么std::vector&lt;customVector&gt; 将连续存储 customVectors,但您仍然会有额外的辅助成员,它们将“中断”customVector 元素数据的实际连续性.

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-11-28
        • 2016-11-09
        • 2014-09-26
        • 2021-04-05
        • 2012-07-20
        • 2019-07-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多