我正在尝试定义一个类。这就是我所拥有的:
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的当前值。然后,即使存储更改,内存也已分配给显示的数组,因此大小不应更改。但是,编译器并不喜欢这样。
对于我如何理解这一点,是否存在真正的误解?有没有人有一个修复程序,可以做我想要的,而无需硬编码数组的大小?
答案 0 :(得分: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;
答案 1 :(得分:2)
您可以使用一维数组。您应该知道二维数组被视为一维数组,当您想要一个可变大小时,您可以使用此模式。例如:
int arr1[ 3 ][ 4 ] ;
int arr2[ 3 * 4 ] ;
它们是相同的,可以通过不同的符号访问其成员:
int x = arr1[ 1 ][ 2 ] ;
int x = arr2[ 1 * 4 + 2 ] ;
当然,arr1可以看作是3行x 4 cols矩阵和3 col x 4行矩阵。 使用这种类型的多维数组,您可以通过单个指针访问它们,但您必须了解其内部结构。它们是一维阵列,它们被视为2维或3维。
答案 2 :(得分: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)]...
这种方法可能看起来很奇怪,但它很强大,对内存有自我管理,而且安全 - 没有边界超限。
答案 3 :(得分:0)
首先,如果要引用2D数组,则必须声明指向指针的指针:
Tile **shown;
然后,看一下错误信息。这是正确的,可理解的英语。它说错误是什么。 Only the first dimension of an allocated array can have dynamic size.
表示 - 猜测,只有已分配数组的第一维可以具有动态大小。而已。如果您希望矩阵具有多个动态维度,请使用C样式malloc()
来维护指针指针,或者对于C ++更好,使用vector
,完全是为了这个目的。
答案 4 :(得分: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
的地址,加上* 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)
编译器知道该怎么做。由于我不记得的原因,将它作为双指针传递通常被认为是更好的做法,但这正是技术层面上发生的事情。