IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

SL & STL C++ Discussion :

[C++/STL] Fonction générique pour supprimer les éléments d'une std::map


Sujet :

SL & STL C++

  1. #1
    Expert confirmé
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Points : 4 388
    Points
    4 388
    Par défaut [C++/STL] Fonction générique pour supprimer les éléments d'une std::map
    Bonjour à tous,

    Après les vectors, j'ai découvert les "map" qui sont bien utiles pour utiliser des collections de type cle/valeur.
    Cependant, je souhaite supprimer tous les éléments de mes maps à l'aide d'une fonction générique et du for_each() :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    struct DeleteBitmap
    {
        void operator()(std::map<int, BITMAP*>* p) const  // on peut modifier le paramètre mais pas le foncteur
        {
            libererBitmap(p->second); // fonction qui libère un BITMAP*
            p = NULL;
        }
    };
     
    // son appel serait : 
    for_each(mapTotale.begin(), mapTotale.end(), DeleteBitmap());
    Problème ca ne compile pas sur la ligne "p->second" car second n'est pas un membre de p (logique)...

    Actuellement, j'utilise une boucle mais je préfèrerais le template :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    map<int, BITMAP*>::iterator pos;
        for (pos = mapTotale.begin(); pos != mapTotale.end(); ++pos)
        {
            libererBitmap(pos->second);
            pos->second = NULL;
        }
    Et cela marche avec la boucle...

    Voilà si vous pouviez me débloquer

    Merci beaucoup ! et bonnes fêtes de fin d'année
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  2. #2
    Membre chevronné
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Points : 2 205
    Points
    2 205
    Par défaut
    for_each passe un un itérateur (qui déréférencé donne un std::pair), pas une map au foncteur hein.. :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
     
    struct print_second
    {
        template <typename T>
    	void operator()(const T& t) const
    	{
    	    std::cout << t.second << std::endl;
    	}
    };
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  3. #3
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    Quelques remarques :
    -> Pour compléter la remarque de Goten, le foncteur ne prend pas le conteneur ni l'itérateur mais le résultat du déréfencement de l'itérateur. Pour un conteneur associatif, c'est effectivement un pair clé/valeur

    -> Dans ton exemple, tu passes l'argument par pointeur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    struct DeleteBitmap
    {
        void operator()(std::map<int, BITMAP*>* p) const  // on peut modifier le paramètre mais pas le foncteur
        {
    Ca n'a pas de sens (sauf à ce que le déréfencement de l'itérateur soit un pointeur). On passe par référence ou par valeur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    struct DeleteBitmap
    {
        void operator()(std::pair<int, BITMAP*>& const p) const  // on peut modifier le paramètre mais pas le foncteur
        {
    -> En C++0x, les lambdas seraient bien utiles :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::for_each(mon_map.begin(),mon_map.end(),[](std::pair<key_t,value_t> val){delete val.second;});
    -> En revanche, tu vas te rendre rapidement compte qu'il ait quand même plus facile d'encapsuler ta ressource dans une enveloppe RAII (pointeur intelligent par expl). Il te suffit alors tout simplement de faire un clear :
    ou d'utiliser l'idiom suivant pour garantir la libération effective des ressources internes du conteneur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::map<key_t,value_t>().swap(mon_map);

  4. #4
    Expert confirmé
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Points : 4 388
    Points
    4 388
    Par défaut
    Merci pour votre aide !

    Une dernière question :

    Est-il nécessaire de faire un NULL après une libération de mùémoire ? c'est vrai que j'aime bien mettre mon pointeru libéré à NULL mais je dois passer par une référence non constante pour le faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template <typename T>
    	void operator()(T& t) const
    	{
    	    libererBitmap(t.second);
    	    t.second = NULL;
    	}
    Ou alors :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template <typename T>
    	void operator()(const T& t) const
    	{
    	    libererBitmap(t.second);
    	}
    Je pense que c'est mieux d'utiliser des référence constante mais j'en suis pas sur, si vous pouviez m'éclairer
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  5. #5
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,

    Si, juste après ton for_each, tu invoque la fonction clear de ta map, tu n'a pas besoin de mettre le pointeur à NULL, vu que, de toutes manières, l'ensemble des élément de la map seront effectivement détruit (et que tu ne pourras donc plus récupérer un itérateur sur l'un d'eux).

    Il est, en effet, préférable de :
    • passer une référence constante chaque fois que faire se peut
    • séparer clairement les responsabilités, en veillant à ce que le foncteur se contente de libérer la mémoire allouée pour le pointeur sur bitmap et
    • en veillant à ce que ce soit la fonction qui décide qu'un pointeur sur bitmap peut être libéré (comprend: qui utilise le foncteur) qui veille à supprimer l'élément de la map

    En dehors du for_each, tu pourrais parfaitement avoir une fonction membre proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
     
        struct deleter
        {
            template<typename T>
            void operator(T * ptr)
            {
                delete ptr;
            }
            template<typename T>
            void operator(std::pair<type,T*> const & pair)
            {
                delete pair.second;
            } 
        };
    void TaClass::releaseRessource(type key)
    {
        std::map<type, Truc *>::iterator it=lamap.find(key);
        if(it!=lamap.end())
        {
            deleter()(it);
            // OU   OU   OU
            deleter (it.second);
            lamap.erase(it);
            // OU OU OU
            lamap.erase(key);
        } 
    }
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  6. #6
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Je déterre ce sujet car j'ai une question existentielle qui est quasiment la même, à la différence près que je n'ai pas des pointeurs. en fait dans mon cas c'est même un poil plus complexe mais le fond revient au même. Prenons le code suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    using namespace std;
    map< string, vector< MyClass > > the_map;
     
    // ici du code qui rempli la map, par exemple
    vector<MyClass> v1 = { MyClass( 0 ), MyClass( 1 ) };
    the_map.insert( make_pai( "v1", v1 ) );
    vector<MyClass> v2 = { MyClass( 2 ), MyClass( 3 ) };
    the_map.insert( make_pai( "v2", v2 ) );
     
    // et maintenant je veux effacer un élément selon une key donnée, prenons "v1" par exemple:
    auto it = the_map.find( "v1");
    if ( it != the_map.end() )
       // là je met quoi?
    Mon problème c'est qu'une map, en mémoire c'est un arbre en fait. Alors comment faire pour supprimer proprement un élément, surtout quand c'est un gros truc? Faut-il utiliser le erase-remove idiom ici? Faut-il faire une copie sélective de la map?
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  7. #7
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par r0d Voir le message
    Mon problème c'est qu'une map, en mémoire c'est un arbre en fait. Alors comment faire pour supprimer proprement un élément, surtout quand c'est un gros truc? Faut-il utiliser le erase-remove idiom ici? Faut-il faire une copie sélective de la map?
    Comme tu l'as si bien fait remarquer, map gère ses éléments dans un rb-tree de pair.

    Tu fais donc simplement lamap.erease(it); et le tour est joué

    std::map fait re balance d'office l'arbre qui contient ses valeurs, aussi bien à l'insertion qu'à l'effacement (la suppression d'un noeud n'implique pas la suppression des noeuds enfants )
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  8. #8
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Okay ça marche impec, merci koala
    Comme prévu, ce n'est pas efficace du tout, mais une map n'est pas un conteneur propice pour la suppression, surtout si ce sont des gros objets qui y sont stockés.
    Mais bon, dans mon cas, cette suppression n'arrive que dans des cas exceptionnels.
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  9. #9
    Membre expérimenté

    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2011
    Messages
    685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2011
    Messages : 685
    Points : 1 418
    Points
    1 418
    Par défaut
    Il y a une petite alternative plus simple : map::erase a une "key-based version"

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    auto it = the_map.find( "v1");
    if ( it != the_map.end() )
      the_map.erase(it);
    équivalent à

    return (je cite) : "For the key-based version (2), the function returns the number of elements erased, which in map containers is at most 1." Du coup si l'élément n'existe pas la fonction renverra 0, sinon 1.

    source : http://www.cplusplus.com/reference/map/map/erase/
    Nullius in verba

  10. #10
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Parfait, je prends. Merci
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. lisp pour supprimer les éléments dans une surface
    Par ocni2000 dans le forum Lisp
    Réponses: 1
    Dernier message: 21/03/2012, 11h08
  2. Fonction générique pour valider les champs d'un formulaire quelconque
    Par dark_vidor dans le forum Général JavaScript
    Réponses: 5
    Dernier message: 26/01/2009, 01h34
  3. pour supprimer les doublons d'une table qcq
    Par lamjed dans le forum Oracle
    Réponses: 7
    Dernier message: 19/12/2008, 16h42
  4. Réponses: 12
    Dernier message: 04/03/2007, 11h43
  5. Réponses: 8
    Dernier message: 29/06/2006, 15h37

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo