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

 C++ Discussion :

Nettoyage efficace et complet d'objets dans un vector


Sujet :

C++

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Mai 2009
    Messages
    62
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2009
    Messages : 62
    Points : 54
    Points
    54
    Par défaut Nettoyage efficace et complet d'objets dans un vector
    Bonsoir ( même s'il est bien tard pour le dire ).
    Tout d'abord voilà le code ( qui compile et qui marche ) :
    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    #include <iostream>
    #include <string>
    #include <vector>
     
     
    using namespace std;
     
    // Listing dynamique d'objets
     
     
    // CLASSES
    class artiste{
        string nom;
        public:
        artiste(string nomE): nom(nomE) {}
        ~artiste() { cout << "Artiste supprime" << endl;}
        void afficheNom(){ cout << nom << endl; }
    };
     
    class peintre : public artiste{
        public:
        int nbPeintures;
        peintre(string nomE , int nbPaintE): artiste(nomE) , nbPeintures(nbPaintE) {}
        ~peintre() { cout << "Peintre supprime" << endl;}
        void afficheNb(){ cout << nbPeintures << endl; }
    };
     
    // FONCTIONS
    void ajoutVectorArtiste(vector<artiste*>& listing){
        string nomE;
        cout << "Nom du nouvel artiste ?" << endl;
        cin >> nomE;
        artiste* pArtiste = new artiste(nomE);
        listing.push_back(pArtiste);
    }
    void ajoutVectorPeintre(vector<artiste*>& listing){
        string nomE;
        int nbPaintE;
        cout << "Nom du nouveau peintre ?" << endl;
        cin >> nomE;
        cout << "Nombre de peintures ?" << endl;
        cin >> nbPaintE;
        peintre* pPeintre = new peintre(nomE,nbPaintE);
        listing.push_back(pPeintre);
    }
     
    void afficheVectorArtiste(vector<artiste*>&listing){
        if(listing.empty()){
            cout << "Liste Vide" << endl;
        } else {
            int i = 1;
            vector<artiste*>::iterator it;
            for(it = listing.begin() ; it < listing.end() ; it++){
                cout << i << " -> ";
                (**it).afficheNom();
                i +=1;
            }
        }
    }
     
    void RAZVectorArtiste(vector<artiste*>& listing){
        cout << "RAZ du listing" << endl;
        vector<artiste*>::iterator it;
        for(it = (listing).begin() ; it < (listing).end() ; it++){
            delete (*it);
        }
        (listing).erase((listing).begin() , (listing).end());
    }
     
     
    int main(){
        vector<artiste*> listing;
     
        // Menu Utilisateur
        bool quit = false;
        int rep = 0;
        while ( quit == false){
            cout << " 1 : Ajout Peintre //// 2 : Ajout Artiste //// 3 : RAZ /// 4 : Affiche Listing /// 0 : Quit" << endl;
            cin >> rep;
            switch(rep){
                case 1 : { ajoutVectorArtiste(listing); break ; }
                case 2 : { ajoutVectorPeintre(listing); break ; }
                case 3 : { RAZVectorArtiste(listing); break ; }
                case 4 : { afficheVectorArtiste(listing); break ;}
                case 0 : quit = true ; break;
                default : cout << "Entree Invalide" << endl; break;
            }
        }
     
     
     
        return 0;
    }

    J'ai plusieurs questions :
    Est ce que ma fonction RAZ est vraiment efficace ? Appeler un erase pour le vector et un delete pour les objets est il "propre" ?

    Si on crée un Peintre que l'on utilise RAZ j'ai remarqué que seul le destructeur de Artiste est appelé et non celui de Peintre. Ça ne me parait pas normal ...

    Et petite question bonus : le default du switch concerne uniquement les caractères du type de la réponse ( si "rep" est int alors problème si le "rep" est char ).
    Exemple ( je sais que je ne suis pas clair ^^' ) : si on fait un switch demandant une réponse en int alors le default concernera tout les int et non les char ?
    Pour éviter le problème de la boucle infinie si on entre un char et non un int il faut obligatoirement ajouter une structure de contrôle lors de la saisie de la réponse ? Ou y'a t'il un moyen plus simple et moins long ?


    Merci d'avance pour vos réponses, n'hésitez pas à dire que le code n'est pas propres ou qu'il est formulé d'une façon lourde et peu conventionelle

    Bonne Nuit !

  2. #2
    Membre émérite

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Points : 2 252
    Points
    2 252
    Par défaut
    Bonsoir,

    Citation Envoyé par Kiwii Voir le message
    J'ai plusieurs questions :
    Est ce que ma fonction RAZ est vraiment efficace ? Appeler un erase pour le vector et un delete pour les objets est il "propre" ?
    Oui c'est propre. En fait c'est même la seule méthode possible. Pour effacer des éléments d'un vecteur qui ont été alloué dynamiquement il faut au préalable faire un delete desssus, sous peine d'avoir une fuite mémoire. (Note quand même que tu peux remplacer "listing.erase(listing.begin(), listing.end());" par le plus bref "listing.clear();" qui est strictement équivalent)

    Citation Envoyé par Kiwii Voir le message
    ]
    Si on crée un Peintre que l'on utilise RAZ j'ai remarqué que seul le destructeur de Artiste est appelé et non celui de Peintre. Ça ne me parait pas normal ...
    En effet bien vu. C'est une erreur très courante : Oublier de rendre le destructeur de la classe Peintre et Artiste virtuel.

  3. #3
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Généralement, dans mon code, on trouve une petite classe template utilitaire nommée object_deleter<> :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    #include <algorithm>
     
    template <class T>
    struct object_deleter
    {
      void operator()(T* p) { delete p; } 
    };
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    // utilisation sur un std::vector<T>
    template <class T> void raz_vecteur(std::vector<T>& v)
    {
      std::for_each(v.begin(), v.end(), object_deleter<T>());
      v.clear();
    }
    Le fait que le compilateur soit capable tout seul de déduire le type T lorsqu'on passe un vecteur en paramètre de la fonction fait que mon code l'utilisant ressemble à :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    std::vector<mon_type> vect;
    raz_vecteur(vect);
    Le code peut être affiné pour traiter tous les conteneurs ; mais il devient un poil plus complexe à lire. A noter que tout conteneur C définissant un constructeur par défaut autorise l'utilisation du code suivant :

    Cette ligne provoque l'échange des données interne à un conteneur avec celle d'un conteneur temporaire. Une fois cette ligne exécutée, other est vide, et le destructeur du conteneur créé est exécuté (donc la mémoire qui avait été réservée par other est libérée). C'est une autre manière de faire une opération qui équivaudrait à other.clear(), mais cette opération est disponible même si la méthode C::clear() n'est pas définie.

    De plus, C::value_type est le type des objets contenu. Mais nous ne voulons pas traiter les cas où value_type n'est un type pointeur - ou plus exactement, dans ce cas, nous ne voulons pas appeler delete sur chaque élément. On va profiter de deux choses pour écrire le code qui ira bien au final :

    * la déduction des types par le compilateur
    * la surcharge de fonction, qui permet de définir des comportements différents si les paramètres de la fonction sont de type différent.

    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
     
    #include <algorithm>
     
    template <class T>
      struct object_deleter
    {
      void operator()(T* p) { delete p; } 
    };
     
    template <class C, class T>
      void clear_content(C& c, T t)
    {
      // ne fait rien...
    }
     
    // version surchargée, si le second paramètre est un pointeur
    template <class C, class T>
      void clear_content(C& c, T* t)
    {
      std::for_each(c.begin(), c.end(), object_deleter<T>());
    }
     
    template <class Container> 
      void raz(Container& container)
    {
      // un type alias, pour simplifier l'écriture de la ligne suivante
      // le typename ici est nécessaire. 
      typedef typename Container::value_type value_type;
     
      // la version de clear_content() appelée dépends du type value_type.
      // si c'est un type pointeur, alors la seconde version sera appelée et
      // les objets seront détruit. Dans le cas contraire, la première version
      // sera appelée, rien ne se passera (le compilateur va détecter que rien
      // ne se passe, et optimisera le code en fonction). 
      clear_content(container, value_type());
     
      // Maintenant, on peut vider le conteneur. 
      Container().swap(container);
    }
    Le code fait automatiquement le tri entre les vecteurs d'objets et les vecteurs de pointeurs sur des objets. Dans les deux cas, il détruit le contenu avant (ou en même temps) qu'il vide le contenant.
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  4. #4
    Membre du Club
    Profil pro
    Inscrit en
    Mai 2009
    Messages
    62
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2009
    Messages : 62
    Points : 54
    Points
    54
    Par défaut
    Merci à vous deux !
    J'avais aperçu l'histoire du destructeur virtel dans la FAQ mais ne sachant pas ce qu'est une fonction virtuelle j'ai abandonné sa lecture ... et pourtant la réponse y était.
    Je crois que c'est le prochain chapitre où je vais voir toutes ces histoires polymorphiques

    Pareil pour les templates, je ne les ai pas encore vu mais ça ne saurait tarder.
    J'ai ptete pas compris les détails mais j'y reviendrai armé de nouvelle connaissances fantastiques

  5. #5
    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
    Citation Envoyé par Emmanuel Deloget Voir le message
    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
     
    #include <algorithm>
     
    template <class T>
      struct object_deleter
    {
      void operator()(T* p) { delete p; } 
    };
     
    template <class C, class T>
      void clear_content(C& c, T t)
    {
      // ne fait rien...
    }
     
    // version surchargée, si le second paramètre est un pointeur
    template <class C, class T>
      void clear_content(C& c, T* t)
    {
      std::for_each(c.begin(), c.end(), object_deleter<T>());
    }
     
    template <class Container> 
      void raz(Container& container)
    {
      // un type alias, pour simplifier l'écriture de la ligne suivante
      // le typename ici est nécessaire. 
      typedef typename Container::value_type value_type;
     
      // la version de clear_content() appelée dépends du type value_type.
      // si c'est un type pointeur, alors la seconde version sera appelée et
      // les objets seront détruit. Dans le cas contraire, la première version
      // sera appelée, rien ne se passera (le compilateur va détecter que rien
      // ne se passe, et optimisera le code en fonction). 
      clear_content(container, value_type());
     
      // Maintenant, on peut vider le conteneur. 
      Container().swap(container);
    }
    Là par contre pour une fois jsuis pas d'accord :p. Le swap trick dans le cas du vector c'est rarement l'opération souhaité... C'est pas pour rien que le vector ne le fait pas automatiquement (question de performances), je ferais plutôt un coup d'idiome erase/remove et si demandé explicitement un coup de swap trick pour réduire la capacity... Mais je ferais pas ça par défaut.
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  6. #6
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par Goten Voir le message
    Là par contre pour une fois jsuis pas d'accord :p. Le swap trick dans le cas du vector c'est rarement l'opération souhaité... C'est pas pour rien que le vector ne le fait pas automatiquement (question de performances), je ferais plutôt un coup d'idiome erase/remove et si demandé explicitement un coup de swap trick pour réduire la capacity... Mais je ferais pas ça par défaut.
    D'accord avec toi ; ceci dit, ici le but est de vider complètement les éléments. On doit pouvoir faire un paramétrage plus fin, en proposant une policy de libération des ressources prises par le conteneur.

    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
     
    template <class Container>
    struct hard_kill
    {
      stattic void kill(Container& container)
      {
        // Vide véritablement le container. Violent, et quelque fois nécessaire.
        Container().swap(container);
      }
    };
     
    template <class Container>
    struct soft_kill
    {
      stattic void kill(Container& container)
      {
        // a de bonne chance de ne pas changer la capacité du conteneur, 
        // dans les cas ou c'est souhaitable (ex: std::vector qui peut être
        // réutilisé par la suite).
        container.clear();
      }
    };
     
    // On peut prévoir d'autres policies, y compris une policy qui ne fait rien
    // ou rempli le vecteur avec des valeurs par défaut : 
    // template <class Container>
    // struct fill_with_default
    // {
    //    static void kill(Container& container)
    //    {
    //       typedef typename Container::value_type value_type;
    //       std::fill(container.begin(), container.end(), value_type()); 
    //    }
    // };
    // Je ne sais pas trop à quoi ça peut servir, mais bon :)
     
    // Par défaut, la stratégie de vidage effectue une opération qui ne désaloue
    // pas nécessairement la mémoire allouée par le conteneur. Utile si le 
    // conteneur est réutilisé dans des conditions similaire
    template <class Container, class KillPolicy = soft_kill> 
      void raz(Container& container)
    {
      // un type alias, pour simplifier l'écriture de la ligne suivante
      // le typename ici est nécessaire. 
      typedef typename Container::value_type value_type;
     
      // la version de clear_content() appelée dépends du type value_type.
      // si c'est un type pointeur, alors la seconde version sera appelée et
      // les objets seront détruit. Dans le cas contraire, la première version
      // sera appelée, rien ne se passera (le compilateur va détecter que rien
      // ne se passe, et optimisera le code en fonction). 
      clear_content(container, value_type());
     
      // Maintenant, on peut vider le conteneur. 
      // ANCIEN CODE : Container().swap(container);
      // NOUVEAU CODE : histoire d'avoir le choix sur la méthode de vidage.
      KillPolicy::kill(container);
    }
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  7. #7
    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
    Tout à fait ce à quoi je pensais, j'ai juste eu la flemme de mettre du code sur mes mots, chose que tu as faites :>
    "Hardcoded types are to generic code what magic constants are to regular code." --A. Alexandrescu

  8. #8
    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,
    J'aurais juste une remarque : une enveloppe RAII à la ressource et cette question perd son sens


    Citation Envoyé par Emmanuel Deloget Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    // On peut prévoir d'autres policies, y compris une policy qui ne fait rien
    // ou rempli le vecteur avec des valeurs par défaut : 
    // Je ne sais pas trop à quoi ça peut servir, mais bon :)
    => je n'ai pas eu le besoin d'un politique qui ne fait rien mais j'ai déjà eu besoin d'une politique qui rempli le conteneur avec une valeur par défaut. Il s'agissait dans le principe d'utiliser un array (donc à taille fixe) mais avec un remplissage dynamique (donc valeur par défaut == valeur vide).

  9. #9
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    Salut,
    J'aurais juste une remarque : une enveloppe RAII à la ressource et cette question perd son sens
    C'est pas toujours facile à faire : dans plein de cas (et surtout si le type T est lourd), on hésitera a faire des copies de l'objet - et du coup, on se retrouve à devoir utiliser des smart pointers (pas forcément une bonne chose ; mais bon, je ne les aime pas, qu'y puis-je ? ) ou à galérer ici et là pour écrire un code qui fonctionne.

    Dans le principe : oui ; dans la pratique : oui mais
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

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

Discussions similaires

  1. suppression d'un pointeur sur un objet dans un vector
    Par Mindiell dans le forum SL & STL
    Réponses: 9
    Dernier message: 07/08/2008, 14h42
  2. Existence d'un objet dans un Vector
    Par FabaCoeur dans le forum Collection et Stream
    Réponses: 5
    Dernier message: 03/03/2008, 20h51
  3. [STL]Suppression d'un objet dans un vector
    Par cssiste dans le forum SL & STL
    Réponses: 10
    Dernier message: 19/07/2007, 14h23
  4. STL - objet dans un vector/deque
    Par ivles dans le forum SL & STL
    Réponses: 11
    Dernier message: 26/02/2007, 11h38
  5. objets dans un vector
    Par anasama dans le forum SL & STL
    Réponses: 2
    Dernier message: 21/04/2006, 10h21

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