我想找到在 C++ 中实现整数的三维数组的安全方法,使用指针算术/动态内存分配,或者使用STL
向量等技术。
本质上,我希望我的整数数组维度看起来像:
[ x ][ y ][ z ]
x 和 y 在 20-6000 范围内 z 是已知的并且等于 4。
我想找到在 C++ 中实现整数的三维数组的安全方法,使用指针算术/动态内存分配,或者使用STL
向量等技术。
本质上,我希望我的整数数组维度看起来像:
[ x ][ y ][ z ]
x 和 y 在 20-6000 范围内 z 是已知的并且等于 4。
看看 Boost多维数组库。这是一个示例(改编自 Boost 文档):
#include "boost/multi_array.hpp"
int main() {
// Create a 3D array that is 20 x 30 x 4
int x = 20;
int y = 30;
int z = 4;
typedef boost::multi_array<int, 3> array_type;
typedef array_type::index index;
array_type my_array(boost::extents[x][y][z]);
// Assign values to the elements
int values = 0;
for (index i = 0; i != x; ++i) {
for (index j = 0; j != y; ++j) {
for (index k = 0; k != z; ++k) {
my_array[i][j][k] = values++;
}
}
}
}
每对方括号都是一个解引用操作(当应用于指针时)。例如,以下代码对是等效的:
x = myArray[4];
x = *(myArray+4);
x = myArray[2][7];
x = *((*(myArray+2))+7);
要使用您建议的语法,您只需取消引用从第一次取消引用返回的值。
int*** myArray = (some allocation method, keep reading);
//
// All in one line:
int value = myArray[x][y][z];
//
// Separated to multiple steps:
int** deref1 = myArray[x];
int* deref2 = deref1[y];
int value = deref2[z];
要分配这个数组,你只需要认识到你实际上没有一个整数的三维数组。你有一个由整数数组组成的数组。
// Start by allocating an array for array of arrays
int*** myArray = new int**[X_MAXIMUM];
// Allocate an array for each element of the first array
for(int x = 0; x < X_MAXIMUM; ++x)
{
myArray[x] = new int*[Y_MAXIMUM];
// Allocate an array of integers for each element of this array
for(int y = 0; y < Y_MAXIMUM; ++y)
{
myArray[x][y] = new int[Z_MAXIMUM];
// Specify an initial value (if desired)
for(int z = 0; z < Z_MAXIMUM; ++z)
{
myArray[x][y][z] = -1;
}
}
}
取消分配此数组遵循与分配类似的过程:
for(int x = 0; x < X_MAXIMUM; ++x)
{
for(int y = 0; y < Y_MAXIMUM; ++y)
{
delete[] myArray[x][y];
}
delete[] myArray[x];
}
delete[] myArray;
下面是一种使用 C 或 C++ 在每个数组的一块内存中创建 3D 数组的简单方法。不需要使用 BOOST(即使它很好),也不需要在具有多个间接的行之间分配分配(这非常糟糕,因为它在访问数据时通常会造成很大的性能损失并且它会碎片化内存)。
唯一要了解的是,没有多维数组之类的东西,只有数组的数组(数组)。最里面的索引是内存中最远的。
#include <stdio.h>
#include <stdlib.h>
int main(){
{
// C Style Static 3D Arrays
int a[10][20][30];
a[9][19][29] = 10;
printf("a[9][19][29]=%d\n", a[9][19][29]);
}
{
// C Style dynamic 3D Arrays
int (*a)[20][30];
a = (int (*)[20][30])malloc(10*20*30*sizeof(int));
a[9][19][29] = 10;
printf("a[9][19][29]=%d\n", a[9][19][29]);
free(a);
}
{
// C++ Style dynamic 3D Arrays
int (*a)[20][30];
a = new int[10][20][30];
a[9][19][29] = 10;
printf("a[9][19][29]=%d\n", a[9][19][29]);
delete [] a;
}
}
对于您的实际问题,由于可能存在两个未知维度,因此我的建议存在问题,它只允许一个未知维度。有几种方法可以管理它。
好消息是使用变量现在可以在 C 中使用,它被称为变长数组。你看这里了解详情。
int x = 100;
int y = 200;
int z = 30;
{
// C Style Static 3D Arrays
int a[x][y][z];
a[99][199][29] = 10;
printf("a[99][199][29]=%d\n", a[99][199][29]);
}
{
// C Style dynamic 3D Arrays
int (*a)[y][z];
a = (int (*)[y][z])malloc(x*y*z*sizeof(int));
a[99][199][29] = 10;
printf("a[99][199][29]=%d\n", a[99][199][29]);
free(a);
}
如果使用 C++,最简单的方法可能是使用运算符重载来坚持数组语法:
{
class ThreeDArray {
class InnerTwoDArray {
int * data;
size_t y;
size_t z;
public:
InnerTwoDArray(int * data, size_t y, size_t z)
: data(data), y(y), z(z) {}
public:
int * operator [](size_t y){ return data + y*z; }
};
int * data;
size_t x;
size_t y;
size_t z;
public:
ThreeDArray(size_t x, size_t y, size_t z) : x(x), y(y), z(z) {
data = (int*)malloc(x*y*z*sizeof data);
}
~ThreeDArray(){ free(data); }
InnerTwoDArray operator [](size_t x){
return InnerTwoDArray(data + x*y*z, y, z);
}
};
ThreeDArray a(x, y, z);
a[99][199][29] = 10;
printf("a[99][199][29]=%d\n", a[99][199][29]);
}
上面的代码访问 InnerTwoDArray 有一些间接成本(但一个好的编译器可能会优化它)但只使用一个内存块来分配在堆上的数组。这通常是最有效的选择。
显然,即使上面的代码仍然简单明了,STL 或 BOOST 也做得很好,因此无需重新发明轮子。我仍然相信知道它可以很容易地完成是很有趣的。
使用向量:
std::vector< std::vector< std::vector< int > > > array3d;
如果元素已经添加,则每个元素都可以通过 array3d[x][y][z] 访问。(例如通过 push_back)
应该注意的是,出于所有意图和目的,您只处理一个二维数组,因为第三个(也是最不重要的)维度是已知的。
如果您事先不知道数组的每个维度中有多少条目,则使用 STL 或 Boost 是非常好的方法,因为它们会给您动态内存分配,如果您的数据集是,我推荐这些方法中的任何一种保持基本不变,或者如果它主要只接收新条目而不是很多删除。
但是,如果您事先了解您的数据集,例如大约总共将存储多少项目,或者如果要稀疏填充数组,则最好使用某种散列/桶函数,并使用XYZ 索引作为您的关键。在这种情况下,假设每个维度不超过 8192(13 位)条目,您可以使用 40 位(5 字节)密钥。或者,假设总是有 4 x Z 条目,您只需使用 26 位 XY 密钥。这是速度、内存使用和动态分配之间更有效的权衡之一。
与使用 new/delete 相比,使用 STL 管理内存有很多优点。如何表示数据的选择取决于您计划如何使用它。一个建议是隐藏实现决策并为一维 STL 向量提供三维 get/set 方法的类。
如果您确实认为需要创建自定义 3d 矢量类型,请先研究 Boost。
// a class that does something in 3 dimensions
class MySimpleClass
{
public:
MySimpleClass(const size_t inWidth, const size_t inHeight, const size_t inDepth) :
mWidth(inWidth), mHeight(inHeight), mDepth(inDepth)
{
mArray.resize(mWidth * mHeight * mDepth);
}
// inline for speed
int Get(const size_t inX, const size_t inY, const size_t inZ) {
return mArray[(inZ * mWidth * mHeight) + (mY * mWidth) + mX];
}
void Set(const size_t inX, const size_t inY, const size_t inZ, const int inVal) {
return mArray[(inZ * mWidth * mHeight) + (mY * mWidth) + mX];
}
// doing something uniform with the data is easier if it's not a vector of vectors
void DoSomething()
{
std::transform(mArray.begin(), mArray.end(), mArray.begin(), MyUnaryFunc);
}
private:
// dimensions of data
size_t mWidth;
size_t mHeight;
size_t mDepth;
// data buffer
std::vector< int > mArray;
};
Pieter 的建议当然很好,但是您必须记住的一件事是,在构建大型阵列的情况下,它可能会很慢。每次向量容量发生变化时,都必须复制所有数据(“n”个向量向量)。