Простой, эффективный слабый указатель, который устанавливается в NULL, когда целевая память освобождается
Есть ли простой, эффективный слабый / защищенный указатель? Мне нужно несколько указателей на один и тот же объект, которые все автоматически устанавливаются в NULL при удалении объекта. Существует один "главный" указатель, который всегда используется для удаления объекта, но может быть несколько других указателей, которые ссылаются на тот же объект.
Вот некоторые решения, которые не совсем соответствуют моим потребностям:
- QPointer: я не занимаюсь разработкой приложения QT; Я не хочу включать эту библиотеку / наследовать от QObject.
- boost:: weak_ptr:
исключениевыдаетсяпри доступе к освобожденному объекту.Слишком дорого для моей ситуации: нормально проверить слабый указатель;Я планирую провести некоторую ручную очистку, когда слабый указатель больше не действует.обновление: слабый_птр может быть протестирован без исключения - Низкие накладные расходы Слабые указатели: это очень близко к тому, что я ищу, за исключением того, что мне не нравится тот факт, что "эта схема гарантированно работает только до тех пор, пока вы не выделите 2**sizeof(int) раза в в том же месте."
Зачем мне нужны эти слабые / охраняемые указатели: у меня есть игра со списком игровых объектов. Некоторые объекты зависят от других, например, объект отладки / статистики, связанный с игровой сущностью. Объект отладки / состояния отображает полезную информацию об игровом объекте, но имеет смысл только тогда, когда игровой объект существует. Поэтому, если игровая сущность удалена, объект отладки / статистики должен это понять и удалить сам. (Другая идея - это отслеживающая ракета: вместо удаления она может искать новую цель.)
Я хочу отделить логику отладки / статистики от игровой сущности. Игровой объект не должен знать, что к нему прикреплен объект отладки / статистики. Хотя я предпочел бы ответ для слабых / защищенных указателей, я также приветствую различные способы решения моей конкретной задачи. Я думаю, что мне, возможно, придется реализовать диспетчер игровых объектов, который отслеживает время жизни объектов и использует дескрипторы вместо необработанных указателей на адреса памяти.
Я занимаюсь разработкой на C++.
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 повторно используется для чего-то другого. Если бы у вас просто был указатель на список указателей, вы бы в итоге получили дескриптор, который указывает на совершенно другой объект, а не на NULL. Таким образом, мы даем каждому объекту глобально уникальный номер и сохраняем его в ручке.
Конечно, вы могли бы использовать вектор std, или карту, или словарь, или какую-то другую структуру данных для таблицы сущностей, но наши требования, как правило, касались постоянной памяти, когерентности кэша и абсолютной максимальной производительности (так как handle_t::Get() вызывается тысячи раз за кадр).