6

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

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

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

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

4

6 回答 6

17

该标准确实要求 a 的内存std::vector是连续的。另一方面,如果您编写如下内容:

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

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

std::vector<double> v;

并计算索引,完全按照您建议使用float. (如果需要,您也可以使用地址创建第二个std::vector<float*>。但是,我一直只是重新计算索引。)

于 2011-06-24T08:26:06.937 回答
5

根据 C++ 标准,保证向量的元素是连续的。
来自标准的报价如下:

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

23.2.6 类模板向量【向量】

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

C++03 标准(23.2.4.1):

向量的元素是连续存储的,这意味着如果 v 是一个向量,其中 T 是除 bool 之外的某种类型,那么对于所有 0 <= n < v,它都遵循恒等式 &v[n] == &v[0] + n 。尺寸()​​。

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

于 2011-06-24T08:22:03.013 回答
5

正如@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,但我在之前的答案中发布了该技术的演示。

于 2011-06-24T08:30:42.990 回答
3

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

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

float (*p)[5] = new float[N][5];

p [0] [0] = 42;   // access first element
p[N-1][4] = 42;   // access last element

delete[] p;

请注意,只有一个分配。我可以建议阅读更多关于在 C++ 中使用数组的信息吗?

于 2011-06-24T09:54:24.493 回答
1

在引擎盖下,一个向量可能看起来大致像(p-code):

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

现在如果你做一个vector<vector<T> >,就会有这样的布局

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

或以“内联”形式

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

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

该标准只要求一个向量的数据是连续的,而不是整个向量。

于 2011-06-24T08:40:50.023 回答
1

创建一个简单的类,如您所说,一个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;

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

但正如你所看到的,它改变了天真的期望,因为它是myarray[y][x].

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

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

于 2011-06-24T10:03:27.920 回答