2010-03-17 3 views
7

Supposons que j'ai une carte STL où les valeurs sont des pointeurs, et je veux les supprimer tous. Comment représenter le code suivant, mais en utilisant std :: for_each? Je suis heureux pour les solutions à utiliser Boost.Comment utiliser for_each pour supprimer toutes les valeurs d'une carte STL?

for(stdext::hash_map<int, Foo *>::iterator ir = myMap.begin(); 
    ir != myMap.end(); 
    ++ir) 
{ 
    delete ir->second; // delete all the (Foo *) values. 
} 

(j'ai trouvé checked_delete de Boost, mais je ne suis pas sûr de savoir comment l'appliquer à la pair<int, Foo *> que le iterator représente).

(De même, pour les besoins de cette question, ignorez le fait que stocker des pointeurs bruts qui ont besoin d'être supprimés dans un conteneur STL n'est pas très judicieux).

Note: J'ai trouvé et listé ci-dessous une réponse d'une ligne ci-dessous ... mais le code est assez horrible, donc j'ai accepté la réponse de GMan.

+0

Joli usage de l'opération de pré-cryptage sur votre itérateur! –

Répondre

14

Vous devez faire un objet de fonction:

struct second_deleter 
{ 
    template <typename T> 
    void operator()(const T& pX) const 
    { 
     delete pX.second; 
    } 
}; 

std::for_each(myMap.begin(), myMap.end(), second_deleter()); 

Si vous utilisez coup de pouce, vous pouvez aussi utiliser la bibliothèque lambda:

namespace bl = boost::lambda; 
std::for_each(myMap.begin(), myMap.end(), second_deleter(), 
       bl::bind(bl::delete_ptr(), 
       bl::bind(std::select2nd<myMap::value_type>(), _1)); 

Mais vous pouvez essayer la bibliothèque pointer containers qui fait cela automatiquement. Notez que vous n'utilisez pas de carte, mais un hash_map. Je vous recommande de passer à unordered_map de boost, ce qui est plus courant. Cependant, il ne semble pas y avoir de ptr_unordered_map.

Pour plus de sécurité, vous devriez envelopper ce truc. Par exemple:

template <typename T, typename Deleter> 
struct wrapped_container 
{ 
    typedef T container_type; 
    typedef Deleter deleter_type; 

    wrapped_container(const T& pContainer) : 
    container(pContainer) 
    {} 

    ~wrapped_container(void) 
    { 
     std::for_each(container.begin(), container.end(), deleter_type()); 
    } 

    T container; 
}; 

et de l'utiliser comme:

typedef wrapped_container< 
      boost::unordered_map<int, Foo*>, second_deleter> my_container; 

my_container.container./* ... */ 

Cela garantit peu importe, votre conteneur sera itérés avec un Deleter. (. Sauf exception, par exemple)

Comparez:

std::vector<int*> v; 
v.push_back(new int); 

throw "leaks!"; // nothing in vector is deleted 

wrapped_container<std::vector<int*> > v; 
v.container.push_back(new int); 

throw "no leaks!"; // wrapped_container destructs, deletes elements 
+0

J'ai pensé que ça pourrait être quelque chose comme ça. J'avais espéré qu'il serait possible de le faire sans définir mon propre type. Est-il possible d'utiliser boost :: bind ou select2nd ou somesuch, je me demande? – stusmith

+0

cela n'appelle-t-il pas techniquement UB si la carte décide de se rééquilibrer et commence donc à copier les pointeurs supprimés? peut-être null eux aussi? - juste remarqué son hash_map pas une carte donc il ne sera pas ré-équilibrage –

+0

@jk: cela ne supprime rien de la carte, juste en supprimant les objets vers lesquels il pointe. –

0

S'il est possible, pourquoi ne pas utiliser des pointeurs intelligents dans votre carte?

+0

Cela devrait être possible à un moment donné. Je suis en train de refactoriser un code plutôt ancien pour le moment. En vérité, j'étais juste curieux de savoir si je pouvais le faire dans une ligne de code. Mais vous avez tout à fait raison, je voudrais finalement y arriver ... mais cela fait partie d'un million de lignes de code de 15 ans. – stusmith

+0

Je vois votre point, mais l'utilisation de pointeurs intelligents ici supprime le besoin de refactoriser et de déboguer la suppression des membres. Un moins de gestion de la mémoire à s'inquiéter d'aller de l'avant. Chaque fois que j'utilise new/delete, je pense vraiment que c'est nécessaire. Une "odeur de code" personnelle (par Martin Fowler), si vous voulez. –

+0

Bien sûr, si votre ancien code renvoie une carte, alors l'approche 'for_each' est probablement votre meilleur pari - mais si vous aviez quelque part dans la création de la carte, je vous recommande d'utiliser des pointeurs intelligents. – Jacob

3

Avez-vous essayé d'utiliser BOOST_FOREACH? Cela devrait vous permettre de faire cela en ligne sans créer votre propre foncteur.

Je n'ai pas testé le code suivant mais il devrait ressembler à ceci (sinon exactement):

typedef stdext::hash_map<int, Foo *> MyMapType; //see comment. 
BOOST_FOREACH(MyMapType::value_type& p, myMap) 
{ 
    delete p.second; 
} 

thats bien plus de 1 ligne, en raison de la typedef :)

+0

Généralement son. Malheureusement, il s'agit d'une macro et ne peut pas gérer les virgules entre '<>' – UncleBens

+1

ah oui, un typedef serait nécessaire, désolé à ce sujet, plus une ligne. – Akanksh

0

OK, J'ai découvert comment le faire en une ligne ... mais je ne pense pas que je ferais jamais ce qui suit en vrai code!

std::for_each(mayMap.begin() 
      , myMap.end() 
      , boost::bind(&boost::checked_delete<Foo> 
          , boost::bind(&stdext::hash_map<int, Foo *>::value_type::second, _1))); 

Cependant, je vais accepter la réponse de GMan parce que j'aime son idée d'un récipient enveloppé, et ma réponse, en dépit d'être une ligne comme l'a demandé, est tout simplement méchant.

Questions connexes