web-dev-qa-db-fra.com

Détruire correctement les pointeurs dans un std :: map

J'ai une carte déclarée comme

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

que j'utilise pour associer le chemin d'un fichier de texture à la texture réelle afin que je puisse référencer la texture par le chemin sans charger la même texture plusieurs fois pour des sprites individuels. Ce que je ne sais pas faire, c'est détruire correctement les textures dans le destructeur de la classe ResourceManager (où se trouve la carte).

J'ai pensé à utiliser une boucle avec un itérateur comme celui-ci:

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

Mais cela ne fonctionne pas, il dit que supprimer attend un pointeur. Il est assez tard, donc je manque probablement quelque chose d'évident, mais je voulais que cela fonctionne avant de se coucher. Alors suis-je proche ou suis-je totalement dans la mauvaise direction avec ça?

28
Mike

En ce qui concerne votre exemple de code, vous devez le faire dans la boucle:

delete itr->second;

La carte comporte deux éléments et vous devez supprimer le second. Dans votre cas, itr->first Est un std::string Et itr->second Est un Texture*.

Si vous devez supprimer une entrée particulière, vous pouvez faire quelque chose comme ceci:

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);
}

Vous devez vous assurer que l'entrée existe dans la carte, sinon vous risquez d'obtenir une exception lorsque vous essayez de supprimer le pointeur de texture.

Une alternative pourrait être d'utiliser std::shared_ptr Au lieu d'un pointeur brut, alors vous pourriez utiliser une syntaxe plus simple pour supprimer un élément de la carte et laissez le std::shared_ptr gérer la suppression de l'objet sous-jacent lorsque cela est approprié. De cette façon, vous pouvez utiliser erase() avec un argument clé, comme ceci:

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

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

Cela fera deux choses:

  • Supprimez l'entrée de la carte, si elle existe
  • S'il n'y a pas d'autres références au Texture*, L'objet sera supprimé

Pour utiliser std::shared_ptr, Vous aurez besoin d'un compilateur C++ 11 récent ou Boost .

42
Roger Rowland

La réponse n'a pas entièrement résolu le problème de la boucle. Au moins, Coverty (TM) ne permet pas d'effacer l'itérateur dans la boucle et de l'utiliser quand même pour continuer la boucle. Quoi qu'il en soit, après avoir supprimé la mémoire, appeler clear () sur la carte devrait faire le reste:

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

Vous n'utilisez pas le bon outil pour le travail.

Les pointeurs ne doivent pas "posséder" les données.

Utilisation boost::ptr_map<std::string, Texture> à la place.

1
Mehrdad