Правильное уничтожение указателей на std :: map

У меня есть карта, объявленная как

std::map<std::string, Texture*> textureMap;

который я использую для сопряжения пути к текстурному файлу с фактической текстурой, поэтому я могу ссылаться на текстуру по пути, не загружая одну и ту же текстуру кучу раз для отдельных спрайтов. То, что я не знаю, как это сделать, - это правильно уничтожить текстуры в деструкторе для класса ResourceManager (где находится карта).

Я думал об использовании цикла с итератором, как это:

ResourceManager::~ResourceManager()
{
    for(std::map<std::string, Texture*>::iterator itr = textureMap.begin(); itr != textureMap.end(); itr++)
    {
        delete (*itr);
    }
}

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

Ответ 1

Что касается вашего примера кода, вам нужно сделать это внутри цикла:

delete itr->second;

Карта состоит из двух элементов, и вам нужно удалить вторую. В вашем случае itr->first является std::string а itr->second - Texture*.

Если вам нужно удалить определенную запись, вы можете сделать что-то вроде этого:

std::map<std::string, Texture*>::iterator itr = textureMap.find("some/path.png");
if (itr != textureMap.end())
{
    // found it - delete it
    delete itr->second;
    textureMap.erase(itr);
}

Вы должны убедиться, что запись существует на карте, иначе вы можете получить исключение при попытке удалить указатель текстуры.

Альтернативой может быть использование std::shared_ptr вместо необработанного указателя, тогда вы можете использовать более простой синтаксис для удаления элемента с карты и позволить std::shared_ptr обрабатывать удаление базового объекта, когда это необходимо. Таким образом, вы можете использовать erase() с ключевым аргументом, например:

// map using shared_ptr
std::map<std::string, std::shared_ptr<Texture>> textureMap;

// ... delete an entry ...
textureMap.erase("some/path.png");

Это будет делать две вещи:

  • Удалите запись с карты, если она существует
  • Если нет других ссылок на Texture*, объект будет удален

Для использования std::shared_ptr вам понадобится компилятор С++ 11 или Boost.

Ответ 3

Ответ не полностью касался проблемы с циклом. По крайней мере, Coverty (TM) не позволяет стереть итератор в цикле и по-прежнему использовать его для продолжения цикла. В любом случае, после удаления памяти, вызов clear() на карте должен сделать все остальное:

ResourceManager::~ResourceManager()
{
    for(std::map<std::string, Texture*>::iterator itr = textureMap.begin(); itr != textureMap.end(); itr++)
    {
        delete (itr->second);
    }
    textureMap.clear();
}