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 :

Arbre binaire et libération récursive de la mémoire


Sujet :

C++

  1. #1
    Futur Membre du Club
    Inscrit en
    Avril 2009
    Messages
    7
    Détails du profil
    Informations forums :
    Inscription : Avril 2009
    Messages : 7
    Points : 7
    Points
    7
    Par défaut Arbre binaire et libération récursive de la mémoire
    Bonjour,

    J'ai un doute sur ce que fait exactement un bout de code que j'ai écrit pour l'école, en fait je crains que toute la mémoire allouée pour mon arbre binaire ne soit pas complètement libérée :

    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
    class Dictionnaire
    {
    private:
        char *mot;
        Dictionnaire *gauche;
        Dictionnaire *droite;
     
        void effacer();
     
    public:
        ~Destructeur();
    /* et d'autres choses ici bien entendu */
    }
     
    void Dictionnaire::effacer()
    {
        delete[] mot;
        if (gauche) gauche->effacer();
        if (droite) droite->effacer();
    }
     
    Dictionnaire::~Dictionnaire()
    {
        effacer();
    }
    Est-ce que de cette manière, la mémoire allouée est bien complètement libérée lors de l'appel du destructeur?
    J'ai un doute en fait, j'ai peur que seule la mémoire allouée pour mot[] soit libérée, et peut-être pas la mémoire de tous les *gauche et *droite.

    J'aurais pu directement faire un destructeur récursif, mais j'ai également une fonction reinitialiser() qui doit aussi libérer la mémoire (et donc faire appel à effacer()).

    Autre question, n'y a-t-il pas de problème avec le C++ et la récursivité si la récursion va trop loin? Je pense qu'au niveau performance il vaut mieux concevoir un algorithme itératif (je pense que fonction récursive = un CALL à chaque appel, mais peut-être que le compilateur arrive à éviter ça), et je crois avoir déjà entendu parler d'histoires de "dépassement de pile" si ça va trop profondément.

    Merci!

  2. #2
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Points : 4 625
    Points
    4 625
    Par défaut
    Ça efface bien tout.
    Par contre, si tu appelles effacer deux fois de suite, comportement indéfini.
    Fais mot = 0 après delete[] mot.
    Boost ftw

  3. #3
    Futur Membre du Club
    Inscrit en
    Avril 2009
    Messages
    7
    Détails du profil
    Informations forums :
    Inscription : Avril 2009
    Messages : 7
    Points : 7
    Points
    7
    Par défaut
    OK ça me rassure alors :-)

    Vu que effacer est en private, à moins que je fasse une erreur d'implémentation, je ne pense pas qu'il y ait un risque de double delete (mais je vais quand même rajouter le =NULL, c'est vrai que c'est une bonne habitude à prendre).

  4. #4
    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
    Bonjour,
    Une façon de te rassurer ne serait-elle pas d'utiliser std::string à la place de char* (si mot désigne bien une chaîne de caractère) ? Tu n'a alors plus à te soucier des allocations :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    class Dictionnaire
    {
    private:
        std::string mot;

  5. #5
    Membre confirmé
    Avatar de gb_68
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2006
    Messages
    232
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2006
    Messages : 232
    Points : 546
    Points
    546
    Par défaut
    Citation Envoyé par loufoque Voir le message
    Ça efface bien tout.
    Attention tout de même aux fils gauche et droite
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
        Dictionnaire *gauche;
        Dictionnaire *droite;
    Je ne sais pas comment tu gères leur mémoire, mais si ils sont censés appartenir au parent Dictionnaire, il faut les libérer.
    Citation Envoyé par pokraka Voir le message
    Vu que effacer est en private, à moins que je fasse une erreur d'implémentation, je ne pense pas qu'il y ait un risque de double delete
    Oui mais tu as parlé d'une fonction reinitialiser() qui est susceptible de l'appeler ; de plus tu appelles directement effacer() sur tes fils (gauche et droite) ce que leur propre destructeur fera aussi (ça fait déjà deux appels ), sauf si tu l'a omis comme je l'ai déjà évoqué plus haut (ce qui produit une fuit mémoire).
    Citation Envoyé par 3DArchi Voir le message
    Bonjour,
    Une façon de te rassurer ne serait-elle pas d'utiliser std::string à la place de char*
    +1

  6. #6
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Franchement, je serais plutôt du genre à faire ceci:
    Code C++ : 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
    class Dictionnaire
    {
    private:
    	std::string mot;
    	Dictionnaire *gauche;
    	Dictionnaire *droite;
     
    	void effacer();
     
    public:
    	~Destructeur();
    	/* et d'autres choses ici bien entendu */
    }
     
    void Dictionnaire::effacer()
    {
    	delete gauche; //if inutile ici, car delete NULL est garanti ne rien faire
    	delete droite; 
    	gauche=NULL;
    	droite=NULL;
    }
     
    Dictionnaire::~Dictionnaire()
    {
    	effacer();
    }
    Ne pas oublier, les pointeurs gauche et droite doivent absolument être initialisés (à NULL ou à une valeur valide) par le constructeur de l'objet.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  7. #7
    Membre éclairé Avatar de befalimpertinent
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    561
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Gironde (Aquitaine)

    Informations forums :
    Inscription : Avril 2007
    Messages : 561
    Points : 833
    Points
    833
    Par défaut
    On ne sait pas exactement qui alloue mot, gauche et droite. J'aurais plutôt tendance à laisser le soin de libérer la mémoire à celui qui l'a crée. Non ?

    Si tu fais:
    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
     
    void f()
    {
      Dictionnaire tronc, g,d;
      tronc.set_gauche(&g);
      tronc.set_droit(&d);
      tronc.effacer();//si delete gauche et/ou droit dans effacer argghhh !
    }
    //et donc plutôt
     
    void f()
    {
      Dictionnaire *tronc = new Dictionaire();
      Dictionnaire *g = new Dictionaire();
      Dictionnaire *d = new Dictionaire();
      tronc->set_gauche(g);
      tronc->set_droit(d);
      tronc->effacer();//si delete gauche et/ou droit dans effacer argghhh !
      delete tronc;
      delete g;
      delete d;
    }
    Linux > *

  8. #8
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Le problème, c'est que ça n'est pas viable: Le dictionnaire étant un conteneur, celui qui l'utilise ne peut pas garder en mémoire ce qu'il a créé et ou.

    Comment ferais-tu pour placer dans le dico des éléments lus en boucle?
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  9. #9
    Membre éclairé Avatar de befalimpertinent
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    561
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Gironde (Aquitaine)

    Informations forums :
    Inscription : Avril 2007
    Messages : 561
    Points : 833
    Points
    833
    Par défaut
    Je suis d'accord mais dans ce cas il faut s'assurer que personne d'autre ne puisse obtenir un pointeur sur un gauche ou un droit.
    Linux > *

  10. #10
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    C'est pour ça que dans un dictionnaire, on sépare généralement en deux classes: Dictionnaire et Noeud (qui peut être une classe imbriquée).
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  11. #11
    Futur Membre du Club
    Inscrit en
    Avril 2009
    Messages
    7
    Détails du profil
    Informations forums :
    Inscription : Avril 2009
    Messages : 7
    Points : 7
    Points
    7
    Par défaut
    En fait, ma classe contient deux autres methodes: void inserer(char*) pour insérer un nouveau mot, et bool chercher(char*) pour renvoyer un mot (c'est l'énoncé qui demande de faire ainsi, idem pour le char* plutôt que string, et le prof aime bien qu'on ne s'éloigne pas trop de ce qu'il a demandé).

    On ne peut pas manipuler soi-même l'arbre binaire ; l'utilisateur n'est pas censé se soucier de l'implémentation choisie (il fait juste Dictionnaire dico; pour créer un dictionnaire).
    En fait, le but de l'exercice est de comparer le temps de construction d'une implémentation avec un arbre et d'une implémentation avec un vecteur trié, pas de faire une classe vraiment utile (par exemple, on ne nous demande même pas de pouvoir supprimer un mot, ou de lister tous les mots du dictionnaire).

    Mais je préfère aussi la solution proposée par Médinoc, je pense que je vais opter pour celle-là.

    Pour la fonction reinitialiser(), elle appelle effacer(), puis elle reconstruit un dictionnaire "vide", qui est en fait un arbre binaire non pas vide mais contenant la seule chaine vide "" à son sommet (ceci afin de ne pas avoir à traiter le cas d'un arbre vide dans les algorithmes).
    Donc si le destructeur est appellé après reinitialiser() il n'y a pas de problème, il détruit juste l'arbre ("" (gauche: NULL) (droite: NULL)), sans détruire une deuxième fois l'arbre déjà détruit.

    Je met le sujet en résolu, ça me semble bon comme ça, merci en tout cas

  12. #12
    Rédacteur

    Avatar de Matthieu Brucher
    Profil pro
    Développeur HPC
    Inscrit en
    Juillet 2005
    Messages
    9 810
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur HPC
    Secteur : Industrie

    Informations forums :
    Inscription : Juillet 2005
    Messages : 9 810
    Points : 20 970
    Points
    20 970
    Par défaut
    pff, c'est quoi ce prof ? Il a déjà une fois programmé en C++ ? Ou même en C moderne ?

    void inserer(const char*) !
    bool chercher(const char*) !

    Et ça, c'est en C, je parle même pas de ce que tu devrais faire en C++ (utiliser des string).
    La meilleure solution serait d'encapsuler tes pointeurs gauche droite sans un boost::scoped_ptr, mais j'imagine que ton prof n'en voudrait pas. Mais marque au moins que ce serait dans un monde professionnel le minimum à faire (tout ce qui a été indiqué ici).

    Après, on se demande pourquoi les applications plantent... Pas étonnant.

    C'était mon coup de gueule du jour

    +1 avec Médinoc

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

Discussions similaires

  1. Commande récursive pour dessiner un arbre binaire.
    Par IKota dans le forum Programmation (La)TeX avancée
    Réponses: 2
    Dernier message: 30/05/2010, 22h41
  2. Allocation de mémoire, arbre binaire
    Par smyley dans le forum Algorithmes et structures de données
    Réponses: 2
    Dernier message: 13/05/2009, 18h19
  3. Afficher un arbre binaire avec sa structure
    Par PhoneKilleR dans le forum C
    Réponses: 7
    Dernier message: 23/04/2008, 23h24
  4. Arbre binaire
    Par Heaven dans le forum Algorithmes et structures de données
    Réponses: 6
    Dernier message: 02/02/2004, 19h01
  5. [LG]probleme de creation arbre binaire
    Par jsaviola dans le forum Langage
    Réponses: 2
    Dernier message: 06/01/2004, 20h57

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