我如何声明一个2d数组使用新?

比如,对于一个“普通”数组,我将:

int* ary = new int[Size]

but

int** ary = new int[sizeY][sizeX]

A)不工作/编译b)没有完成什么:

int ary[sizeY][sizeX] 

所做的事。


当前回答

在c++ 11及以上版本中,我推荐两种通用技术,一种用于编译时维度,另一种用于运行时维度。这两个答案都假设您需要统一的二维数组(而不是锯齿状数组)。

编译时维度

使用std::array的std::array,然后使用new把它放到堆上:

// the alias helps cut down on the noise:
using grid = std::array<std::array<int, sizeX>, sizeY>;
grid * ary = new grid;

同样,这仅适用于在编译时已知维度大小的情况。

运行时维度

实现只有在运行时才知道大小的二维数组的最佳方法是将其包装到一个类中。该类将分配一个1d数组,然后重载操作符[]来为第一个维度提供索引。 这是因为在c++中,2D数组是行为主的:

(摘自http://eli.thegreenplace.net/2015/memory-layout-of-multi-dimensional-arrays/)

连续的内存序列有利于提高性能,而且易于清理。下面是一个示例类,省略了很多有用的方法,但显示了基本思想:

#include <memory>

class Grid {
  size_t _rows;
  size_t _columns;
  std::unique_ptr<int[]> data;

public:
  Grid(size_t rows, size_t columns)
      : _rows{rows},
        _columns{columns},
        data{std::make_unique<int[]>(rows * columns)} {}

  size_t rows() const { return _rows; }

  size_t columns() const { return _columns; }

  int *operator[](size_t row) { return row * _columns + data.get(); }

  int &operator()(size_t row, size_t column) {
    return data[row * _columns + column];
  }
}

因此,我们用std::make_unique<int[]>(行*列)项创建一个数组。我们重载操作符[],它将为我们索引行。它返回一个int *,指向该行的开头,然后可以像对列一样对该行进行解引用。注意,make_unique在c++ 14中首次发布,但如果需要,可以在c++ 11中填充它。

对于这些类型的结构,重载operator()也是很常见的:

  int &operator()(size_t row, size_t column) {
    return data[row * _columns + column];
  }

从技术上讲,我在这里没有使用new,但是从std::unique_ptr<int[]>移动到int *并使用new/delete是很简单的。

其他回答

int** ary = new int[sizeY][sizeX]

应该是:

int **ary = new int*[sizeY];
for(int i = 0; i < sizeY; ++i) {
    ary[i] = new int[sizeX];
}

然后清理是:

for(int i = 0; i < sizeY; ++i) {
    delete [] ary[i];
}
delete [] ary;

编辑:正如Dietrich Epp在评论中指出的那样,这并不是一个轻量级的解决方案。另一种方法是使用一个大内存块:

int *ary = new int[sizeX*sizeY];

// ary[i][j] is then rewritten as
ary[i*sizeY+j]

2D数组基本上是一个指针的1D数组,其中每个指针都指向一个1D数组,该数组将保存实际数据。

这里N是行,M是列。

动态分配

int** ary = new int*[N];
  for(int i = 0; i < N; i++)
      ary[i] = new int[M];

fill

for(int i = 0; i < N; i++)
    for(int j = 0; j < M; j++)
      ary[i][j] = i;

打印

for(int i = 0; i < N; i++)
    for(int j = 0; j < M; j++)
      std::cout << ary[i][j] << "\n";

free

for(int i = 0; i < N; i++)
    delete [] ary[i];
delete [] ary;

我在创建动态数组时使用这个。如果你有一个类或结构。这是可行的。例子:

struct Sprite {
    int x;
};

int main () {
   int num = 50;
   Sprite **spritearray;//a pointer to a pointer to an object from the Sprite class
   spritearray = new Sprite *[num];
   for (int n = 0; n < num; n++) {
       spritearray[n] = new Sprite;
       spritearray->x = n * 3;
  }

   //delete from random position
    for (int n = 0; n < num; n++) {
        if (spritearray[n]->x < 0) {
      delete spritearray[n];
      spritearray[n] = NULL;
        }
    }

   //delete the array
    for (int n = 0; n < num; n++) {
      if (spritearray[n] != NULL){
         delete spritearray[n];
         spritearray[n] = NULL;
      }
    }
    delete []spritearray;
    spritearray = NULL;

   return 0;
  } 

试着这样做:

int **ary = new int* [sizeY];
for (int i = 0; i < sizeY; i++)
    ary[i] = new int[sizeX];

这个答案的目的不是添加其他答案没有涵盖的新内容,而是扩展@Kevin Loney的答案。

你可以使用轻量级声明:

int *ary = new int[SizeX*SizeY]

访问语法将是:

ary[i*SizeY+j]     // ary[i][j]

但这对大多数人来说都很麻烦,可能会导致混乱。所以,你可以这样定义宏:

#define ary(i, j)   ary[(i)*SizeY + (j)]

现在可以使用非常相似的语法ary(i, j) //表示ary[i][j]。 这具有简单美观的优点,同时,使用表达式代替索引也更简单,不那么令人困惑。

要访问,比如说,ary[2+5][3+8],你可以写ary(2+ 5,3 +8),而不是看起来复杂的ary[(2+5)*SizeY +(3+8)],也就是说,它节省了括号,有助于可读性。

警告:

尽管语法非常相似,但并不相同。 如果将数组传递给其他函数,则必须以相同的名称传递SizeY(或者声明为全局变量)。

或者,如果你需要在多个函数中使用数组,那么你可以在宏定义中添加SizeY作为另一个参数,如下所示:

#define ary(i, j, SizeY)  ary[(i)*(SizeY)+(j)]

你懂的。当然,这会变得太长而没有用处,但它仍然可以防止+和*的混淆。

当然不推荐这样做,大多数有经验的用户会谴责这是一种糟糕的做法,但我还是忍不住要分享它,因为它很优雅。

编辑: 如果你想要一个适用于任意数量数组的可移植解决方案,你可以使用以下语法:

#define access(ar, i, j, SizeY) ar[(i)*(SizeY)+(j)]

然后你可以使用访问语法将任意大小的数组传递给调用:

access(ary, i, j, SizeY)      // ary[i][j]

附注:我已经测试了这些,在g++14和g++11编译器上可以使用相同的语法(作为左值和右值)。