Как удалить указатель, который не был назначен объекту

0

Я новичок в C++, и сейчас я работаю над игрой. В игре у меня будет случайное вражеское поколение с течением времени, и я использую std :: vector, чтобы сделать это. Я думал, что могу использовать что-то вроде

enemies.push_back(new Enemy(random_x, randomY));

в пределах цикла, но теперь мне кажется, что это может привести к утечке памяти, поскольку я не удаляю указатели. Поэтому я хотел бы узнать, что я должен сделать, чтобы предотвратить утечку памяти. должен ли я найти способ удалить всех врагов, которых я создаю, или должен ли я сделать что-то совершенно другое?

  • 0
    Сделайте это в (итератор) цикле над всеми элементами во enemies .
  • 0
    В чем причина смерти ваших врагов? Они умирают в цикле, который проверяет определенные ситуации? Пожалуйста, поделитесь этим кодом :)
Показать ещё 5 комментариев
Теги:
pointers
vector
game-engine

2 ответа

3
Лучший ответ

Существуют различные способы решения этой проблемы.

Вы можете использовать необработанные указатели, как в std::vector<Enemy*>, тогда вам нужно удалить их при их удалении:

delete *pos; // pos is an iterator pointing to the element to be deleted
enemies.erase(pos);

Однако это не способ обрабатывать вещи в C++.

Вы можете использовать shared_ptr если хотите передать "врагов", не имея доступа к списку, как в std::vector<std::shared_ptr<Enemy>>. Затем вам просто нужно стереть, и память будет автоматически освобождена для вас, как только никакая shared_ptr указывающая на врага, больше не будет существовать.

enemies.erase(pos); // Nothing more needed, if someone still has a shared_ptr-copy 
                    // of the enemy it will stay in memory until that reference is gone

Если вы всегда обращаетесь к врагу с помощью списка, вы можете даже использовать std::unique_ptr. Но в этом случае может быть даже лучше просто перейти с простейшей из всех форм: std::vector<Enemy>. Таким образом, вам никогда не придется называть new. Но это также мешает вам использовать полиморфизм, и копии могут быть очень дорогими в зависимости от вашего Enemy -class.

Почему полиморфизм не работает?

std::vector<Enemy> хранит полный Enemy в нем, копируя его. Если, например, Enemy занимает 8 байтов в памяти, и у вас есть 5 врагов в векторе, теоретически он займет 40 байт памяти (может потребоваться больше, но это специфично для реализации)

| Idx0  | Idx1  | Idx2  | Idx3  | Idx4 |
[Enemy1][Enemy2][Enemy3][Enemy4][Enemy5]
   8b      8b      8b      8b      8b

Если вы попытаетесь сохранить производный объект ExtraToughEnemy с добавленными атрибутами в позиции 3, который требует, скажем, 4 байта, он не будет работать, потому что каждая запись в векторе должна иметь тот же размер.

| Idx0  | Idx1  | Idx2  | Idx3  | Idx4 |
[Enemy1][Enemy2][ExtraToughEnemy3][Enemy4][Enemy5]
   8b      8b           12b          8b      8b

Если вы используете указатели, вы избегаете этой проблемы, так как каждый объект-указатель имеет одинаковый размер, но может указывать на разные производные объекты.

|  Idx0  |  Idx1  |  Idx2  |  Idx3  |  Idx4 |
[Enemy1*][Enemy2*][Enemy3*][Enemy4*][Enemy5*]
   4b       4b       4b       4b       4b

Указатель базового слова может указывать на любой базовый класс или производный объект. Вы должны будете использовать бросок для доступа к дополнительным атрибутам Enemy3*.

Примечание об общих указателях и уникальных указателях:

Если вы их используете, вы можете рассмотреть (в зависимости от вашего компилятора), даже пропустить с помощью new вообще и использовать std::make_shared или std::make_unique для создания объектов в куче.

  • 0
    Спасибо, это очень помогло. Но я не понимаю, как использование std::vector<Enemy> может удержать меня от полиморфизма. Вы можете это объяснить?
  • 0
    Я отредактирую ответ.
Показать ещё 2 комментария
0

Там только утечка памяти, если вы потеряете доступ к объекту, не освободив сначала хранилище резервных копий. Это не так, потому что вы призываете найти всех этих врагов, просто пройдя через коллекцию enemies.

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

  • 0
    Да, я понимаю, но удаление элементов списка - это та часть, которую я не совсем понимаю, я полагаю. Например, если бы у меня были объекты для всех врагов, таких как Enemy enemy1; было бы просто удалить их, просто вызовите delete enemy1 . Но так как это несовместимо с динамическим списком и большим количеством врагов, которые я создаю, мне было интересно, смогу ли я что-то сделать, например, delete enemies.at(i) в цикле, когда я закончу с ними.
  • 0
    @MertcanEkiz, вы можете сделать это, вроде. Идея состоит в том, чтобы вытащить указатель из вектора, сделать то, что нужно с ним, а затем удалить его.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню