2

我正在尝试定义一个类。这就是我所拥有的:

enum Tile {
GRASS, DIRT, TREE 
};

class Board {
public:
    int toShow;
    int toStore;
    Tile* shown;
    Board (int tsh, int tst);
    ~Board();
};

Board::Board (int tsh, int tst) {
    toShow = tsh;
    toStore = tst;
    shown = new Tile[toStore][toStore]; //ERROR!
}

Board::~Board () {
    delete [] shown;
}

但是,我在指示的行上收到以下错误——只有分配数组的第一个维度可以具有动态大小。

我想要做的不是硬编码,而是将参数 toShow 传递给构造函数并创建一个二维数组,该数组只包含我想要显示的元素。

但是,我的理解是,当构造函数被调用,并且显示被初始化时,它的大小会被初始化为toStore的当前值。然后即使 toStore 发生变化,内存已经分配给显示的数组,因此大小不应该改变。但是,编译器不喜欢这样。

我如何理解这一点是否存在真正的误解?有没有人有一个修复程序可以做我想做的事情,而不必对数组的大小进行硬编码?

4

5 回答 5

3

使用 C++ 的容器,这就是它们的用途。

class Board {
public:
    int toShow;
    int toStore;
    std::vector<std::vector<Tile> > shown;
    Board (int tsh, int tst) : 
      toShow(tsh), toStore(tst), 
      shown(tst, std::vector<Tile>(tst))
    {
    };
};

...

    Board board(4, 5);
    board.shown[1][3] = DIRT;
于 2012-06-04T19:08:32.457 回答
2

您可以使用一维数组。您应该知道,二维数组被视为一维数组,当您需要可变大小时,可以使用此模式。例如 :

int arr1[ 3 ][ 4 ] ;
int arr2[ 3 * 4 ] ;

它们是相同的,并且可以通过不同的符号访问它们的成员:

int x = arr1[ 1 ][ 2 ] ;
int x = arr2[ 1 * 4 + 2 ] ;

当然 arr1 可以看作是一个 3 行 x 4 列矩阵和 3 列 x 4 行矩阵。使用这种类型的多维数组,您可以通过单个指针访问它们,但您必须了解其内部结构。它们是一维数组,它们被视为 2 维或 3 维。

于 2012-06-04T19:14:37.273 回答
1

让我告诉你当我需要一个 3D 数组时我做了什么。这可能有点过头了,但它很酷并且可能会有所帮助,尽管这是一种完全不同的方式来做你想做的事。

我需要表示一个 3D 单元格框。只有一部分细胞被标记并且是有意义的。有两种选择可以做到这一点。第一个,声明一个具有最大可能大小的静态 3D 数组,如果盒子的一个或多个维度小于静态数组中的相应维度,则使用其中的一部分。

第二种方法是动态分配和解除分配数组。使用 2D 阵列非常费劲,更不用说 3D 了。

阵列解决方案定义了一个 3D 阵列,其中感兴趣的单元格具有特殊值。大多数分配的内存是不必要的。

我双向倾倒。相反,我转向了 STL 地图。我定义了一个名为 Cell 的结构体,它有 3 个成员变量 x、y、z 表示坐标。构造函数Cell(x, y, z)用于轻松创建这样的 Cell。我定义了operator <它以使其可订购。然后我定义了一个map<Cell, Data>. 只需通过以下方式将坐标为 x、y、z 的标记单元格添加到地图

my_map[Cell(x, y, z)] = my_data;

这样我就不需要维护 3D 数组内存管理,而且实际上只创建了所需的单元格。

检查坐标 x0、y0、z0 处的调用是否存在(或标记)是通过以下方式完成的:

map<Cell, Data>::iterator it = my_map.find(Cell(x0, y0, z0));
if (it != my_map.end()) { ...

在坐标 x0、y0、z0 处引用单元格的数据是通过以下方式完成的: my_map[Cell(x0, y0, z0)]...

这种方法可能看起来很奇怪,但它是健壮的,关于内存的自我管理,并且安全 - 没有边界溢出。

于 2012-06-04T19:28:06.347 回答
0

首先,如果你想引用一个二维数组,你必须声明一个指向指针的指针:

Tile **shown;

然后,查看错误消息。这是正确的,可以理解的英语。它说明了错误是什么。Only the first dimension of an allocated array can have dynamic size.意思是——猜猜看,只有分配数组的第一个维度可以具有动态大小。就是这样。如果您希望您的矩阵具有多个动态维度,请使用 C 样式malloc()来维护指向指针的指针,或者,对于 C++ 来说,使用vector, 甚至更好地为此目的而制作。

于 2012-06-04T18:57:29.753 回答
0

了解一点 C 和 C++ 中内存分配的工作原理是很好的。

char x[10];

编译器将分配十个字节并记住起始地址,也许它在0x12(在现实生活中可能更大的数字。)

x[3] = 'a';

x[3]现在编译器通过获取 的起始地址来查找x,即0x12,并添加3*sizeof(char),从而带来0x15。所以x[3]住在0x15.

这个简单的加法运算就是如何访问数组内的内存。对于二维数组,数学只是稍微复杂一些。

char xy[20][30];

从某个地方开始分配 600 个字节,也许是0x2000. 现在访问

xy[4][3];

需要一些数学... xy[0][0], xy[0][1], xy[0][2]...将占用前 30 个字节。然后xy[1][0], xy[1][1], ...将占用字节31到60。它的乘法: xy[a][b]将位于地址xy,加上a * 20,加上b。

这只有在编译器知道第一个维度有多长的情况下才有可能——您会注意到编译器需要知道数字“20”才能进行此数学运算。

现在函数调用。编译器很少关心你是否调用

foo(int* x);

或者

foo(int[] x);

因为在任何一种情况下它都是一个字节数组,你传递起始地址,编译器可以做额外的事情来找到它所在的位置x[3]或任何东西。但是在二维数组的情况下,编译器需要知道20上面示例中的幻数。所以

foo(int[][] xy) {
    xy[3][4] = 5;     //compiler has NO idea where this lives 
                      //because it doesn't know the row dimension of xy!
}

但是如果你指定

foo(int[][30] xy)

编译器知道该做什么。由于某些原因,我不记得将它作为双指针传递通常被认为是更好的做法,但这就是技术层面的情况。

于 2012-06-04T19:09:33.030 回答