Простой, эффективный слабый указатель, который устанавливается равным нулю при освобождении целевой памяти


Существует ли простой, эффективный слабый / защищенный указатель? мне нужно несколько указателей на один и тот же объект, которые все автоматически устанавливаются в NULL, когда объект удаляется. Существует один" главный " указатель, который всегда используется для удаления объекта, но может быть несколько других указателей, ссылающихся на тот же объект.

Вот некоторые решения, которые не совсем соответствуют моим потребностям:

  • QPointer : я не разрабатываю приложение QT; я не хочу включать это libary / производное от QObject.
  • boost:: weak_ptr: исключение возникает при обращении к освобожденному объекту. Слишком дорого для моей ситуации: это должно быть нормально, чтобы проверить слабый указатель; я планирую сделать некоторую ручную очистку, когда слабый указатель больше не действителен. update : weak_ptr можно протестировать без исключения
  • слабые указатели с низкими накладными расходами : это очень близко к тому, что я ищу, за исключением того, что мне не нравится тот факт, что " это схема гарантированно работает только до тех пор, пока вы не Выделите 2**sizeof(int) раз в одном и том же месте."

Почему мне нужны эти слабые / защищенные указатели: У меня есть игра со списком игровых объектов. Некоторые объекты зависят от других, например объект отладки/статистики, связанный с игровой сущностью. Объект debug / status отображает полезную информацию о сущности игры, но она имеет смысл только в том случае, если сущность игры существует. Так что если игровой объект удален, то объект debug / stats должен осознать это и удалить себя. (Другая идея-это ракета слежения: вместо того, чтобы удалиться, она может искать новую цель.)

Я хочу, чтобы логика отладки/статистики была отделена от сущности игры. Игровой объект не должен знать, что к нему присоединен объект отладки/статистики. Хотя я бы предпочел ответ для слабых / защищенных указателей, я также приветствую различные способы подхода к моей конкретной задаче. Я думаю, что мне, возможно, придется реализовать игровой объект диспетчер , который отслеживает время жизни объекта и использует дескрипторы вместо необработанных указателей на адреса памяти.

Я развиваюсь в C++.

2 8

2 ответа:

Вы можете использовать lock() член boost::weak_ptr, чтобы иметь возможность проверить (а затем использовать) значение weak_ptr, не имея дело с исключениями.

Это обычная вещь в разработке игр. Обычно используется система дескрипторов объектов, а не слабые указатели Boost, потому что нам нужно, чтобы базовая таблица поиска была постоянной памятью, а также потому, что иногда нам нужна дополнительная информация или гарантии, которые Boost не получил.

Обычный подход заключается в использовании детализации указателей на указатели. На объект ссылается дескриптор, а не указатель. Дескриптор-это индекс в большом массиве указателей на сущности. Когда сущность умирает, она обнуляет указатель в своей таблице сущностей.

struct handle_t
{
   uint32 serialnumber;  // this is a GUID for each entity; it increases 
                         // monotonically over the life of the process
   uint   entityindex;
   inline Entity *Get();
}

struct entityinfo_t
{
   Entity *pEntity;  // an entity's destructor NULLs this out on deletion
   uint32  serialnumber;
}

entityinfo_t g_EntityTable[MAX_ENTITIES];

Entity *handle_t::Get() 
{
  entityinfo_t &info = g_EntityTable[entityIndex];
  if ( serialnumber == info.serialnumber )  
  {
     return info.pEntity;
  }
  else
  {
      return NULL;
  }
}
Серийный номер необходим, потому что массив имеет постоянный размер - в конечном счете, вам нужно будет повторно использовать записи таблицы сущностей, и есть вероятность, что вы можете хранить дескриптор, скажем, индекса #743, достаточно долго, чтобы объект был удален, а ячейка #743 повторно использована для чего-то другого. Если бы у вас был просто указатель на список указателей, вы бы в конечном итоге получили дескриптор, который указывает на совершенно другой объект вместо того, чтобы стать нулем. Таким образом, мы даем каждой сущности глобально уникальный номер и храним его в дескрипторе. Конечно, для таблицы сущностей можно использовать вектор std, или карту, или словарь, или какую-то другую структуру данных, но наши требования обычно сводятся к постоянной памяти, когерентности кэша и абсолютной максимальной производительности (так как handle_t::Get() вызывается тысячи раз за кадр).