8 votes

La bonne façon de détruire une carte qui contient des valeurs de pointeurs

J'utilise std::map pour faire correspondre des valeurs de chaînes de caractères à des MyType * . Ma déclaration de carte ressemble à ceci :

map<string, MyType *> *my_map = new map<string, MyType>;

my_map est une variable membre privée d'une de mes classes. Mon problème est que je ne sais pas comment détruire la carte. Lors de la suppression de la carte, j'aimerais également appeler delete sur l'ensemble des MyType * contenues dans la carte. Voici mon destructeur actuel :

my_map->erase(my_map->begin(), my_map->end());
delete my_map;

Cela supprimera-t-il les pointeurs contenus dans la carte, ou dois-je itérer à travers la carte pour supprimer chaque pointeur avant d'appeler erase ?

9voto

Kylotan Points 14114

Les pointeurs ne font que pointer. Lorsque vous utilisez des pointeurs bruts, vous devez savoir à quelle partie de votre application appartiennent les ressources vers lesquelles les pointeurs pointent. S'ils appartiennent à la carte, vous devrez itérer sur la carte et appeler delete sur chaque pointeur avant que la carte ne soit détruite. Mais si la carte ne contient que des pointeurs vers des objets appartenant à d'autres parties de votre code, vous n'avez rien à faire.

Une solution plus sûre consiste à utiliser des shared_ptr pour gérer la durée de vie de l'objet, ce qui garantit que l'objet est supprimé correctement lorsque le dernier shared_ptr est détruit. Vous pouvez stocker les shared_ptrs dans la map et si aucune autre instance de shared_ptr ne fait référence aux objets dans la map, les objets seront détruits lorsque la map sera détruite, comme vous le souhaitez.

4voto

Michael Kristofik Points 16035

Si vous utilisez pointeurs intelligents au lieu de pointeurs bruts, tout sera nettoyé automatiquement.

// header:
using MapType = std::map<std::string, std::shared_ptr<MyType>>;
shared_ptr<MapType> my_map;

// usage:
my_map.emplace("foo", std::make_shared<MyType>());

// destructor:
MyClass::~MyClass()
{
    // nothing!
}

4voto

John Dibling Points 56814

Cela supprimera-t-il les pointeurs contenus dans la carte [...] ?

Non, étant donné le code que vous avez fourni, vous ferez fuir tous les membres de la carte.

En règle générale, pour chaque new il doit y avoir une correspondance delete . Vous avez un delete pour la carte, mais aucune pour les éléments qu'elle contient.

La solution la plus correcte à ce problème est de ne pas utiliser l'allocation dynamique. Il suffit de stocker MyType si possible :

map<string, MyType>

... et au lieu d'allouer dynamiquement le fichier map lui-même, le stocke automatiquement :

map<string,MyType> my_map;

Si la durée de stockage automatique n'est pas possible pour une raison quelconque, utilisez un pointeur intelligent pour les allocations dynamiques. Avec un compilateur C++11, utilisez unique_ptr (ou, rarement, shared_ptr ou même weak_ptr ) pour les éléments de la map :

map<string, unique_ptr<MyType>> my_map;

(Si l'on dispose d'un compilateur C++03, il faut utiliser les équivalents Boost). my_map est détruit, tous les éléments seront delete d.

En dépit de tout cela, si vous vous trouvez dans une situation où aucune des solutions ci-dessus ne fonctionne pour vous (ce que je soupçonne fortement), vous devrez itérer la carte vous-même :

struct deleter
{
  template <typename T> operator() (const T& rhs) const
  { 
    delete rhs.second;
  }
};

for_each (my_map->begin(), my_map->end(), deleter());

En C++11, on pourrait en faire un lambda, quelque chose du genre :

for_each (my_map->begin(), my_map->end(), [](auto item) -> void
{
  delete item.second;
});

2voto

Mr.C64 Points 11681

En C++ moderne, il suffit de se simplifier la vie et d'utiliser des pointeurs sólo si cela est strictement nécessaire.

Vous avez commencé par ce code :

map<string, MyType *> *my_map = new map<string, MyType>;

La première chose à faire est d'envisager l'utilisation d'un std::map comme membre de données, au lieu d'une instance de pointeur à elle.

Ensuite, si MyType n'est pas très coûteuse à copier et ses instances n'appartiennent qu'à la carte. map de string a MyType (au lieu de MyType* ) :

// my_map data member - no pointers --> automatically deleted in class destructor
map<string, MyType> my_map;

Si vous avez vraiment besoin d'une carte contenant des pointeurs, envisagez d'utiliser pointeurs intelligents comme std::shared_ptr (disponible en C++11/14) pour la propriété partagée, ou std::unique_ptr pour une propriété unique et non partagée.
(Si vous ciblez C++98/03, une option consiste à utiliser boost::shared_ptr . Puisqu'il n'y a pas de sémantique de déplacement, vous ne pouvez pas avoir de unique_ptr qui s'appuie fortement sur la fonction de sémantique des déplacements).
par exemple :

// Map containing _smart_ pointers 
//     --> default destructor is fine (no need for custom delete code)
map<string, shared_ptr<MyType>> my_map;

Comme vous pouvez le constater, l'utilisation de sémantique des valeurs (au lieu de pointeurs bruts), ou pointeurs intelligents vous pouvez simplifier votre code et utiliser la fonction destruction automatique fournie par le C++.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X