Допустим, в моей игре есть монстр, который может взорвать камикадзе на игрока. Давайте наугад выберем имя для этого монстра: Creeper. Итак, у Creeper
класса есть метод, который выглядит примерно так:
void Creeper::kamikaze() {
EventSystem::postEvent(ENTITY_DEATH, this);
Explosion* e = new Explosion;
e->setLocation(this->location());
this->world->addEntity(e);
}
События не ставятся в очередь, они отправляются немедленно. Это приводит Creeper
к удалению объекта где-то внутри вызова postEvent
. Что-то вроде этого:
void World::handleEvent(int type, void* context) {
if(type == ENTITY_DEATH){
Entity* ent = dynamic_cast<Entity*>(context);
removeEntity(ent);
delete ent;
}
}
Поскольку Creeper
объект удаляется, когда kamikaze
метод все еще работает, при попытке доступа к нему происходит сбой this->location()
.
Одно из решений - поместить события в буфер и отправить их позже. Это распространенное решение в играх C ++? Это похоже на хак, но это может быть просто из-за моего опыта работы с другими языками с различными методами управления памятью.
Есть ли в C ++ лучшее общее решение этой проблемы, когда объект случайно удаляет себя из одного из своих методов?
источник
autorelease
в Objective-C, где удаления задерживаются до "чуть-чуть".Ответы:
Не удаляйте
this
Даже неявно.
- Когда-либо -
Удаление объекта, пока одна из его функций-членов все еще находится в стеке, напрашивается на неприятности. Любая архитектура кода, которая приводит к такому случаю («случайно» или нет), объективно плоха , опасна и должна быть немедленно подвергнута рефакторингу . В этом случае, если вашему монстру будет разрешено вызывать World :: handleEvent, ни при каких обстоятельствах не удаляйте монстра внутри этой функции!
(В этой конкретной ситуации мой обычный подход состоит в том, чтобы монстр установил на себе флаг «мертвый», а объект World - или что-то в этом роде - проверял этот флаг «мертвый» один раз за кадр, удаляя эти объекты. из своего списка объектов в мире, и либо удаляя его, либо возвращая его в пул монстров, либо что угодно. В это время мир также рассылает уведомления об удалении, поэтому другие объекты в мире знают, что монстр имеет прекратил существование и может отбросить на него любые указатели, которые они могут содержать. Мир делает это в безопасное время, когда он знает, что в данный момент не обрабатываются никакие объекты, поэтому вам не нужно беспокоиться о том, что стек разворачивается в точку где указатель «this» указывает на свободную память.)
источник
Вместо того, чтобы ставить в очередь события в буфере, ставьте в очередь удаления в буфере. Отложенное удаление может значительно упростить логику; вы можете на самом деле освободить память в конце или начале кадра, когда вы знаете, что с вашими объектами ничего интересного не происходит, и удалить из любого места.
источник
NSAutoreleasePool
Objective-C будет в этой ситуации. Возможно, придется сделатьDeletionPool
с C ++ шаблоны или что-то.Вместо того, чтобы позволить миру обрабатывать удаление, вы могли бы позволить экземпляру другого класса служить в качестве корзины для хранения всех удаленных объектов. Этот конкретный экземпляр должен прослушивать
ENTITY_DEATH
события и обрабатывать их так, чтобы он ставил их в очередь. Затем ониWorld
могут выполнить итерации по этим экземплярам и выполнить операции после смерти после рендеринга кадра и «очистить» этот сегмент, который, в свою очередь, будет выполнять фактическое удаление экземпляров объектов.Пример такого класса будет выглядеть так: http://ideone.com/7Upza
источник
World
классе.Я бы предложил реализовать фабрику, которая используется для всех распределений игровых объектов в игре. Таким образом, вместо того, чтобы называть себя новым, вы бы сказали фабрике создать что-то для вас.
Например
Всякий раз, когда вы хотите удалить объект, фабрика помещает объект в буфер, который очищается в следующем кадре. Задержка уничтожения очень важна в большинстве сценариев.
Также рассмотрите возможность отправки всех сообщений с задержкой в один кадр. Существует только пара исключений, когда вам нужно отправить немедленно, в подавляющем большинстве случаев, однако просто
источник
Вы можете реализовать управляемую память в C ++ самостоятельно, поэтому при
ENTITY_DEATH
вызове все, что происходит, - это уменьшение количества ссылок на него.Позже, как @John предложил в начале каждого кадра, вы можете проверить, какие объекты бесполезны (те, с нулевыми ссылками), и удалить их. Например, вы можете использовать
boost::shared_ptr<T>
( задокументировано здесь ) или если вы используете C ++ 11 (VC2010)std::tr1::shared_ptr<T>
источник
std::shared_ptr<T>
не технические отчеты! - Вам нужно будет указать пользовательский удалитель, иначе он также удалит объект немедленно, когда счетчик ссылок достигнет нуля.Используйте пул и на самом деле не удаляйте объекты. Вместо этого измените структуру данных, в которой они зарегистрированы. Например, для рендеринга объектов есть объект сцены и все сущности, каким-то образом зарегистрированные в нем для рендеринга, обнаружения столкновений и т. Д. Вместо удаления объекта отсоедините его от сцены и вставьте в пул мертвых объектов. Этот метод не только предотвратит проблемы с памятью (например, удаление самого объекта), но также может ускорить вашу игру, если вы правильно используете пул.
источник
В игре мы использовали новое размещение
eventPool был просто большим массивом памяти, который был разделен, и указатель на каждый сегмент был сохранен. Так что alloc () вернет адрес свободного блока памяти. В нашем eventPool память обрабатывается как стек, поэтому после отправки всех событий мы просто сбрасываем указатель стека обратно в начало массива.
Из-за того, как работает наша система событий, нам не нужно было вызывать деструкторов на evetns. Таким образом, пул просто зарегистрирует блок памяти как свободный и выделит его.
Это дало нам огромную скорость.
Кроме того ... Мы фактически использовали пулы памяти для всех динамически выделяемых объектов в процессе разработки, так как это был отличный способ обнаружения утечек памяти, если в пуле оставались какие-либо объекты при выходе из игры (обычно), то, скорее всего, утечка памяти.
источник