C ++ Получение соседей ячейки в сетке, -1x, выбрасывающий нулевое исключение при проверке, если! = NULL - PullRequest
1 голос
/ 19 марта 2019

недавно перешел с 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;
        }
    }
}

Теперь я собираю метод, который возвращает соседей ячейки в массиве плиток, и проверяю, не равен ли каждый сосед 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;
}

Ответы [ 2 ]

2 голосов
/ 19 марта 2019

Более «практичный» способ (более короткий код, который позволяет избежать условных проверок) - создать массив плиток, чтобы он содержал дополнительную «границу» плиток вокруг допустимой области. Если какая-либо позиция тайла находится в допустимой области, то она действительна и так же.

Вы можете иметь специальный тип для плиток границы, который есть только у них, и просто включить эти плитки в список "соседей". Если в вашем мире есть стены, то граница может состоять из материала стен.

Само собой разумеется, вы никогда не должны запрашивать список соседей плитки границы. Это обеспечивается логикой, такой как недопущение того, чтобы плитка границы была допустимой позицией для чего-либо.

Эта плитка находится в допустимой области внутри границы "- это условие, которое легче проверить, в меньшем количестве мест, и ваша программа может быть структурирована так, что эта проверка на самом деле является просто съемным утверждением (проверка ситуации, которая не должно происходить, если программа правильная, а не проверка на ожидаемую ситуацию).

В C и C ++ мы можем сместить указатели так, чтобы позиция [0][0] все еще была углом допустимой области, но координаты вне 101 * * являются допустимыми индексами, как и [w][h].

Во-первых, массиву столбцов выделяется на два элемента больше, чем необходимо, а указатель увеличивается на единицу. Затем столбцам выделяется на два элемента больше, и каждый указатель увеличивается на единицу перед назначением в основной массив.

При освобождении массивов с помощью delete [] вы должны помнить, чтобы уменьшить каждый указатель на единицу.

2 голосов
/ 19 марта 2019

tiles[x][y+1], если y является максимально допустимым значением, не будет NULL, кроме как по милости. Это выходит за пределы, и как только вы выходите за пределы, все ставки прекращаются. Вы вызвали Неопределенное поведение , и почти все может произойти. Даже то, что вы ожидали.

То же самое относится к сообщенному сайту аварии, 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);

Редактировать: украсть это у Боба для полноты

Невозможно вернуть необработанный массив из функции. Массив выходит из области видимости и указатель на него становится недействительным. Либо передайте массив в качестве другого параметра, либо используйте std::array или std::vector, оба из которых могут быть возвращены. Благодаря Copy Elision интеллектуальный компилятор, вероятно, уменьшит затраты на копирование.

Пример: * 1 034 *

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.

...