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

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

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

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

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

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

Я разрабатываю на С++.


person Leftium    schedule 09.12.2009    source источник
comment
Общий вопрос: является ли Qt правильным выбором для разработки игры?   -  person Julius F    schedule 09.12.2009
comment
Слабый указатель Boost позволяет вам проверить, действителен ли он. Он выдает исключение только в том случае, если вы пытаетесь разыменовать его напрямую — аналогично тому, что происходит, если вы пытаетесь разыменовать нулевой указатель.   -  person jalf    schedule 09.12.2009
comment
C++11 имеет std::weak_ptr. Похоже на тот, что у Boost.   -  person Palec    schedule 03.08.2017


Ответы (2)


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

person Michael Burr    schedule 09.12.2009
comment
Можете взглянуть на std::weak_ptr, теперь С++ 11 отсутствует. - person wheredidthatnamecomefrom; 09.03.2020

Это обычное дело в разработке игр. Обычно используется система дескрипторов объектов, а не слабые указатели 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 повторно используется для чего-то другого. Если бы у вас был просто указатель на список указателей, вы бы получили дескриптор, указывающий на совершенно другой объект, а не на NULL. Таким образом, мы присваиваем каждой сущности глобально уникальный номер и также сохраняем его в дескрипторе.

Конечно, вы можете использовать стандартный вектор, или карту, или словарь, или какую-либо другую структуру данных для таблицы сущностей, но наши требования обычно заключались в постоянной памяти, когерентности кэша и абсолютной максимальной производительности (поскольку handle_t::Get () вызывается тысячи раз за кадр).

person Crashworks    schedule 09.12.2009