C++ 获取网格中单元格的邻居,-1x 在检查时抛出 null 异常!= NULL

C++ Getting neighbours of a cell in a grid, -1x throwing null exception when checked if != NULL

最近从 C# 转到 C++,所以我对指针和引用等不熟悉。

我有一个这样声明的指针到指针数组

enum Type
{
    Void,
    DeepWater,
    Water,
    ... etc }

Tile::Type** tiles;

TileManager::TileManager(int width, int height)
{
    this->tiles = new Tile::Type*[width];

    for (int w = 0; w < width; w++)
    {
        tiles[w] = new Tile::Type[height];

        for (int h = 0; h < height; h++)
        {
            tiles[w][h] = Tile::Type::Dirt;
        }
    }
}

现在我正在组合一个方法,该方法 returns tiles 数组中一个单元格的邻居,并检查每个邻居是否不等于 NULL。 然而,即使在检查它是否为 null 时似乎也会引发错误,所以我很困惑。

Tile::Type * TileManager::GetNeighbours(int x, int y)
{
    Tile::Type neighbours[8];

    if(tiles[x][y+1] != NULL)
        neighbours[0] = tiles[x    ][y + 1];

    ...etc

    if (tiles[x - 1][y - 1] != NULL)    //<-- Error fires here
        neighbours[5] = tiles[x - 1][y - 1];

    return neighbours;
}

我知道它为什么会抛出错误,但不敢检查 X 和 Y 以查看它们是否超过限制或低于 0...我认为有一种更实用的方法可以防止这种情况发生,所以我最好问问.

编辑:

谢谢你,user4581301。我在其他地方找到了大部分代码并对其进行了调整以反映您建议的更改。

std::array<Tile::Type, 8> TileManager::GetNeighbours(int c, int r)
{
    std::array<Tile::Type, 8> neighbours;

    const int y[] = { -1, -1, -1,  1, 1, 1,  0, 0 };// 8 shifts to neighbors
    const int x[] = { -1,  0,  1, -1, 0, 1, -1, 1 };// used in functions 

    for (int i = 0; i < 8; ++i)// visit the 8 spaces around it
        if (inField(r + y[i], c + x[i]))
            neighbours[i] = tiles[r + y[i]][c + x[i]];
        else
            neighbours[i] = Tile::Type::Void;

    return neighbours;
}

bool TileManager::inField(int r, int c)
{
    if (r < 0 || r >= 25) return false;
    if (c < 0 || c >= 25) return false;
    return true;
}

tiles[x][y+1],如果 y 是最大有效值,将不会是 NULL,除非是 .这超出了范围,一旦您超出范围,所有赌注都将取消。您已经调用了 Undefined Behaviour 并且几乎任何事情都可能发生。甚至你期望发生的事情。

同样适用于报告的坠机现场,tiles[x - 1][y - 1]

编辑:遗漏了解决方案。没有帮助。

唯一的方法,除了从轨道上起飞并摧毁整个站点,是测试索引以确保它在使用数组上的索引之前不会穿透数组边界。您可能需要一个函数来处理这个问题。

void assign_if(Type & neighbour, int x, int y)
{
    if(x >= 0 && x < width && y >= 0 && y < height)
    neighbour = tiles[x][y];
}

并称​​之为

assign_if(neighbours[0], x, y+1);

以后

assign_if(neighbours[0], x-1, y-1);

编辑:为了完整性从 Bob__ 中窃取了这个

不可能从函数中 return 原始数组。数组超出范围,指向它的指针变得无效。将数组作为另一个参数传入或使用 std::array or std::vector, both of which can be returned. Thanks to Copy Elision,智能编译器可能会消除复制成本。

示例:

std::array<Tile::Type, 8> TileManager::GetNeighbours(int x, int y)
{
    std::array<Tile::Type, 8> neighbours;
    ...
    return neighbours;
}

原发者编辑。这是我的解决方案:

std::array<Tile::Type, 8> TileManager::GetNeighbours(int c, int r)
{
    std::array<Tile::Type, 8> neighbours;

    const int y[] = { -1, -1, -1,  1, 1, 1,  0, 0 };// 8 shifts to neighbors
    const int x[] = { -1,  0,  1, -1, 0, 1, -1, 1 };// used in functions 

    for (int i = 0; i < 8; ++i)// visit the 8 spaces around it
        if (inField(r + y[i], c + x[i]))
            neighbours[i] = tiles[r + y[i]][c + x[i]];
        else
            neighbours[i] = Tile::Type::Void;

    return neighbours;
}

bool TileManager::inField(int r, int c)
{
    if (r < 0 || r >= 25) return false;
    if (c < 0 || c >= 25) return false;
    return true;
}

编辑:警告

这个答案直接解决了所问的问题。请参阅 Kaz 的回答,了解更实用的解决方案的描述,该解决方案以一点内存为代价,完全消除了测试和生成 neighbours 数组的需要。

更多 "practical" 方法(避免条件检查的较短代码)是创建切片数组,以便它在有效区域周围包含额外的 "border" 切片。如果任何图块位置在有效区域中,则有效,因此也是有效的。

您可以为只有它们才有的边框图块创建一个特殊类型,然后将这些图块简单地包含在 "neighbors" 列表中。如果你的世界有墙,那么边界可以由墙组成 material.

不用说,您绝不能询问边界瓦片的邻居列表。这是由逻辑确保的,例如不允许边框图块成为任何内容的有效位置。

This tile is in the valid area within the border”是一个更容易检查的条件,在更少的地方,你的程序可以结构化,这样这个检查实际上只是一个可移动的断言(检查一个如果程序正确则不应发生的情况,而不是检查预期情况)。

在C和C++中,我们可以置换指针,使位置[0][0]仍然是有效区域的角点,而越界坐标[-1][-1]是有效的索引,如是 [w][h].

首先,给列数组分配了比需要的大两个元素,指针加一。然后为列分配两个更大的元素,并且每个指针在分配到主数组之前递增 1。

当使用 delete [] 释放数组时,您必须记住将每个指针减一。