Std::shared_ptr не обновляет свой счетчик

Я пытаюсь создать сеть пользовательского class Tile внутри другого class Board, в котором хранится std::array<std::shared_ptr<Tile>, 34>. Я использую std::shared_ptr<Tile> объекты внутри class Tile, чтобы показать, какие узлы связаны. Однако, когда я инициализирую каждый из 34 объектов Tile, еще не инициализированные объекты, включенные в список, не обновляются с nullptr на свою собственную ссылку.

#include <memory>
#include <array>
#include <cstddef>

struct Tile;
template <const std::size_t N> using TileLink  = std::array<const std::shared_ptr<const Tile>, N>;
template <const std::size_t N> using TileArray = std::array<std::shared_ptr<const Tile>, N>;

struct Tile {
    /* surrounding tiles */
    const std::size_t ID;
    const std::size_t num_adj;
    const TileLink<6> adjacent;
    Tile (std::size_t id, std::size_t n, const TileLink<6> & arr) : ID (id), num_adj (n), adjacent (arr) { }
    ~Tile (void) = default;
};

class Board {
private:
    TileArray<34> tiles;
public:
    Board (void);
    ~Board (void) = default;
};

Board :: Board (void) {
    /* Column 1 */
    tiles[0]  = std::make_shared<Tile> ( 0, 3, TileLink<6>{ tiles[1],  tiles[4],  tiles[5],  nullptr,   nullptr,   nullptr   });
    tiles[1]  = std::make_shared<Tile> ( 1, 4, TileLink<6>{ tiles[0],  tiles[2],  tiles[5],  tiles[6],  nullptr,   nullptr   });
    tiles[2]  = std::make_shared<Tile> ( 2, 3, TileLink<6>{ tiles[1],  tiles[6],  tiles[7],  nullptr,   nullptr,   nullptr   });
    /* Column 2 */
    tiles[3]  = std::make_shared<Tile> ( 3, 2, TileLink<6>{ tiles[4],  tiles[9],  nullptr,   nullptr,   nullptr,   nullptr   });
    tiles[4]  = std::make_shared<Tile> ( 4, 5, TileLink<6>{ tiles[0],  tiles[3],  tiles[5],  tiles[9],  tiles[10], nullptr   });
    tiles[5]  = std::make_shared<Tile> ( 5, 6, TileLink<6>{ tiles[0],  tiles[1],  tiles[4],  tiles[6],  tiles[10], tiles[11] });
    tiles[6]  = std::make_shared<Tile> ( 6, 6, TileLink<6>{ tiles[1],  tiles[2],  tiles[5],  tiles[7],  tiles[11], tiles[12] });
    tiles[7]  = std::make_shared<Tile> ( 7, 5, TileLink<6>{ tiles[2],  tiles[6],  tiles[8],  tiles[12], tiles[13], nullptr   });
    tiles[8]  = std::make_shared<Tile> ( 8, 2, TileLink<6>{ tiles[7],  tiles[13], nullptr,   nullptr,   nullptr,   nullptr   });
    /* Columns 4 - 6 */
    ...
    ...
    ...
    /* Column 7 */
    tiles[31] = std::make_shared<Tile> (31, 3, TileLink<6>{ tiles[26], tiles[27], tiles[32], nullptr,   nullptr,   nullptr   });
    tiles[32] = std::make_shared<Tile> (32, 4, TileLink<6>{ tiles[27], tiles[28], tiles[31], tiles[33], nullptr,   nullptr   });
    tiles[33] = std::make_shared<Tile> (33, 3, TileLink<6>{ tiles[28], tiles[29], tiles[32], nullptr,   nullptr,   nullptr   });
}

std::array я передаю конструктору Tile не меняется. Как бы мне заставить его измениться?

Любая помощь будет принята с благодарностью. Извините за такой длинный вопрос, я действительно не знал, как сделать это меньшим примером.

Сомневаюсь, что кто-то будет смотреть код. Опубликуйте Макви, который показывает проблему с кодом как можно меньше.

user1810087 02.04.2019 14:08

Вы можете использовать ссылку или указатель на него. Или референсная обертка, например std::ref.

Thomas Lang 02.04.2019 14:08

зачем вообще хранить TileLink в shared_ptr? Пока существует доска, плитки существуют, и ссылки должны быть действительными. И если это 2D-доска, а ссылки на плитки — это просто соседи, вы можете просто сохранить их в 2D-сетке.

Zaiborg 02.04.2019 14:31

Указание void в качестве списка параметров Board (void); является C-измом. В C++ используйте Board ();, так как в C++ отсутствие параметров означает «нулевые параметры», тогда как в C отсутствие параметров означает «неизвестное количество параметров».

Eljay 02.04.2019 14:31

@Zaiborg спасибо за понимание, доска - это сетка, в которой я храню плитки, но сетка не прямоугольная (она ближе к шестиугольной) и, следовательно, странные соединения.

Z4ckZ3r0 02.04.2019 15:35

@Элджей, это не совсем относится к проблеме, которую я демонстрирую. Если у вас есть какие-либо мысли по заданному мною вопросу, пожалуйста, оставьте комментарий. Но просто для того, чтобы нам было ясно, что Board (void) является совершенно допустимым синтаксисом C++ для примера, который я привел, и только потому, что что-то является C-синтаксисом, не означает, что он неправильный или хуже.

Z4ckZ3r0 02.04.2019 15:38

Вот почему я оставил это как комментарий, а не ответ.

Eljay 02.04.2019 15:49

с шестнадцатеричными досками для этого есть отличный сайт: redblobgames.com/grids/hexagons там также объясняется, как вы можете хранить свои значения в массиве 2d, а также находить пути и соседей

Zaiborg 03.04.2019 12:03
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
8
119
2

Ответы 2

Здесь:

tiles[0]  = std::make_shared<Tile> ( 0, 3, TileLink<6>{ tiles[1],  tiles[4],  tiles[5],  nullptr,   nullptr,   nullptr   });

tiles[1], tiles[4] и т. д. еще не инициализированы и имеют shared_ptr значение nullptr. Когда вы инициализируете tiles[1] позже, это не меняет того, что внутри tiles[0].

Вы совершенно неправильно используете shared_ptr. Поскольку ваш массив плиток имеет фиксированный размер, просто выделите один раз всю необходимую память, а затем используйте указатели, не являющиеся владельцами. Потому что есть владелец памяти и это Board.

Я согласен с первой частью ответа. А вот второе не понятно написано. Я думаю, вы имеете в виду следующее: TileArray должно быть std::array<const Tile, N> и TileLink должно быть std::array<Tile const * const, N>, верно? На самом деле это не сработает из-за константности и конструкторов Tile. Но я согласен, что shared_ptr здесь совершенно неуместно. Может быть, больше TileArray как std::array<std::unique_ptr<const Tile>, N> и TileLink как std::array<Tile const * , N>, но проблема в том, что указатели TileLink не являются константами.

t.niese 02.04.2019 14:50

Хорошо, так что std::shared_ptr<> в этом сценарии неправильно, и я должен использовать только Tile объекты в массиве в Board с уникальными ссылками на объект в соседнем списке. Но как тогда мне инициализировать список в массиве в конструкторе Board?

Z4ckZ3r0 02.04.2019 15:29

Некоторая константность должна уйти. Даже конструктор не может правильно построить объект (это действительно проблема ОП). Удалите константность, используйте один массив для хранения ваших плиток, обновите все ссылки в теле ctor. Затем объявите свою доску как: const Board theBoard; и с точки зрения клиентов все будет константно, если это действительно то, что вы хотите... (но я сомневаюсь в этом)

fjardon 02.04.2019 16:27

Я решил свою проблему - спасибо @t.niese и @fjardon за вашу помощь.

Мне пришлось сделать Tile более сложным, чтобы облегчить реальную проблему здесь: я не смог создать уникальный Tile объект из std::array в классе. Поэтому я добавил конструктор копирования и оператор присваивания, удалил умные указатели и const-ность и сделал массив типа класса Tile.

class Tile;
template <const std::size_t N> using TileLink  = std::array<const Tile *, N>;
template <const std::size_t N> using TileArray = std::array<Tile, N>;

enum class OWNERSHIP { PLAYER_1, PLAYER_2, CONTESTED };

class Tile {
private:
    OWNERSHIP territory;
    /* surrounding tiles */
    std::size_t ID;
    std::size_t num_adj;
    TileLink<6> adjacent = { nullptr };
public:
    Tile (void) { }
    Tile (OWNERSHIP own, std::size_t id, std::size_t num, const TileLink<6> & arr);
    Tile (const Tile & rhs);
    Tile & operator = (Tile other);
    ~Tile (void) = default;
};

class Board {
private:
    TileArray<34> tiles;
    void create_col_1_and_7 (void);
    void create_col_2 (void);
    ...
    void create_col_6 (void);
public:
    Board (void);
    ~Board (void) = default;
};

Каждая из инициализаций теперь выглядит так:

tiles[0]  = Tile (OWNERSHIP::PLAYER_2,   0, 3, TileLink<6>{ &tiles[1],  &tiles[4],  &tiles[5],  nullptr,    nullptr,    nullptr    });
tiles[1]  = Tile (OWNERSHIP::CONTESTED,  1, 4, TileLink<6>{ &tiles[0],  &tiles[2],  &tiles[5],  &tiles[6],  nullptr,    nullptr    });
...
tiles[32] = Tile (OWNERSHIP::CONTESTED, 32, 4, TileLink<6>{ &tiles[27], &tiles[28], &tiles[31], &tiles[33], nullptr,    nullptr    });
tiles[33] = Tile (OWNERSHIP::PLAYER_1,  33, 3, TileLink<6>{ &tiles[28], &tiles[29], &tiles[32], nullptr,    nullptr,    nullptr    });

Другие вопросы по теме