【问题标题】:std::vector and contiguous memory of multidimensional arraysstd::vector 和多维数组的连续内存
【发布时间】:2011-09-21 20:23:06
【问题描述】:

我知道标准不会强制std::vector分配连续的内存块,但所有的实现都遵循这一点。

假设我希望创建一个多维静态数组的向量。为简单起见,考虑二维和长度为 N 的向量。也就是说,我希望创建一个包含 N 个元素的向量,例如 int[5]

我可以确定所有 N*5 整数现在在内存中都是连续的吗?这样我原则上可以通过知道第一个元素的地址来访问所有整数?这个实现是否依赖?

作为参考,我目前在连续内存块中创建 2D 数组的方式是首先创建一个长度为 N 的 float* (动态)数组,将所有 N*5 个浮点数分配到一个数组中,然后复制每个数组的地址float* 的第一个数组中的第 5 个元素。

【问题讨论】:

  • 我知道标准不强制std::vector分配连续的内存块It does, starting from C++03.
  • @KennyTM:不知道它不在 C++98 中。谢谢。我想这仍然是一个实际要求,以满足元素访问的规定操作复杂性要求,对吧?就像std::string 在实践中一直有连续的元素存储一样,尽管直到 C++0x 才明确规定它。

标签: c++ memory-management vector standards contiguous


【解决方案1】:

标准确实要求std::vector的内存是 连续的。另一方面,如果你写这样的东西:

std::vector<std::vector<double> > v;

全局内存(所有v[i][j])将不连续。这 创建二维数组的常用方法是使用单个

std::vector<double> v;

并完全按照您对float 的建议来计算索引。 (您也可以使用地址创建第二个std::vector&lt;float*&gt; 如果你想。但是,我总是只是重新计算索引。)

【讨论】:

  • +1,对于初始方法,您可以在 C++FAQ lite 中考虑这个example
  • 大卫:很好的参考。每个喜欢“2D Arrays”的人都应该阅读它和以下条目,直到他们可以在睡梦中重复它;-)
【解决方案2】:

根据 C++ 标准,保证向量的元素是连续的。
标准引用如下:

来自 n2798(C++0x 草案):

23.2.6 类模板向量[vector]

1 向量是支持随机访问迭代器的序列容器。此外,它还支持(摊销)恒定时间的最后插入和擦除操作;在中间插入和擦除需要线性时间。存储管理是自动处理的,但可以给出提示以提高效率。向量的元素是连续存储的,这意味着如果 v 是一个向量,其中 T 是除 bool 之外的某种类型,那么对于所有 0

C++03 标准 (23.2.4.1):

向量的元素是连续存储的,这意味着如果 v 是一个向量,其中 T 是 bool 以外的某种类型,那么它对所有的都服从恒等式 &v[n] == &v[0] + n 0

另外,请参阅 here Herb Sutter 对此的看法。

【讨论】:

  • 万岁 std::vector :D :D :D
  • 但是C++0X还不是官方标准。
【解决方案3】:

正如@Als 已经指出的那样,是的,std::vector(现在)保证连续分配。但是,我不会用指针数组模拟二维矩阵。相反,我会推荐两种方法之一。 (到目前为止)更简单的方法是仅使用 operator() 进行下标,然后进行乘法以将 2D 输入转换为向量中的线性地址:

template <class T>
class matrix2D { 
     std::vector<T> data;
     int columns;
public:
     T &operator()(int x, int y) {
         return data[y * columns + x];
     }

     matrix2D(int x, int y) : data(x*y), columns(x) {}
};

如果出于某种原因,您想使用matrix[a][b] 样式寻址,您可以使用代理类来处理转换。虽然它是用于 3D 矩阵而不是 2D,但我在 previous answer 上发布了该技术的演示。

【讨论】:

    【解决方案4】:

    作为参考,我目前在连续内存块中创建 2D 数组的方式是首先创建一个长度为 N 的 float* (动态)数组,将所有 N*5 个浮点数分配到一个数组中,然后复制每个数组的地址float* 的第一个数组中的第 5 个元素。

    这不是二维数组,而是指针数组。如果你想要一个真正的二维数组,可以这样做:

    float (*p)[5] = new float[N][5];
    
    p [0] [0] = 42;   // access first element
    p[N-1][4] = 42;   // access last element
    
    delete[] p;
    

    注意只有一个分配。我可以建议阅读更多关于using arrays in C++ 的内容吗?

    【讨论】:

    • std::vector&lt;std::array&lt;float, 5&gt; &gt;
    • @Tom:嗯,看起来很好吃!
    【解决方案5】:

    在底层,一个向量可能看起来大概像(p-code):

    class vector<T> {
        T      *data;
        size_t  s;
    };
    

    现在如果你做一个vector&lt;vector&lt;T&gt; &gt;,就会有这样的布局

    vector<vector<T>> --> data {
        vector<T>,
        vector<T>,
        vector<T>
    };
    

    或以“内联”形式

    vector<vector<T>> --> data {
        {data0, s0},
        {data1, s1},
        {data2, s2}
    };
    

    是的,向量向量因此使用连续内存,但不,不是你想要的。它很可能存储一个指向外部位置的指针数组(和一些其他变量)。

    标准只要求向量的数据是连续的,不要求向量作为一个整体。

    【讨论】:

      【解决方案6】:

      创建一个简单的类,如您所说,一个 2D 数组,类似于:

      template <class T> 2DArray {
      private:
          T *m_data;
          int m_stride;
      public:
          2DArray(int dimY, int dimX) : m_stride(dimX) : m_data(new[] T[dimX * dimY]) {}
          ~2DArray() { delete[] m_data; }
          T* operator[](int row) { return m_data + m_stride * row; }
      }
      

      可以这样使用:

      2DArray<int> myArray(30,20);
      
      for (int i = 0; i < 30; i++)
          for (int j = 0; j < 20; j++)
              myArray[i][j] = i + j;
      

      或者甚至将&amp;myArray[0][0] 作为地址传递给采用某种“平面缓冲区”的低级函数。

      但正如您所见,它改变了幼稚的期望,因为它是 myarray[y][x]

      一般来说,如果您与需要某种经典 C 风格平面数组的代码交互,那么为什么不直接使用呢?

      编辑:如前所述,上面是简单。没有任何边界检查尝试。就像,“一个数组”。

      【讨论】:

        猜你喜欢
        • 2019-07-27
        • 1970-01-01
        • 2017-03-21
        • 2014-08-12
        • 2022-01-24
        • 2014-03-27
        • 1970-01-01
        • 2018-02-03
        • 1970-01-01
        相关资源
        最近更新 更多