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 :

Un destructeur qui n'est pas appelé ?


Sujet :

C++

  1. #1
    Nouveau membre du Club
    Homme Profil pro
    Lycéen
    Inscrit en
    Octobre 2014
    Messages
    51
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Arabie Saoudite

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Octobre 2014
    Messages : 51
    Points : 35
    Points
    35
    Par défaut Un destructeur qui n'est pas appelé ?
    Bonjour à tous,

    J'ai créé une classe template, et j'ai mis dans le destructeur des std::cout afin de comprendre l'ordre d'appel du destructeur. Mais il s'avère qu'aucune phrase n'est affichée dans la console. Je ne comprends pas trop pourquoi.

    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
    template <typename T> class arbre
    {  
        T data;
        std::vector<C*> fils;
    public:
        arbre(){}
        arbre(T d):data(d){}
        // des méthodes
     
        void transformeEnVector(std::vector<arbre<T>*>& v)const
        {
            std::cout<<std::endl<<std::endl<<"blalalalalala"<<std::endl<<std::endl;
            v.push_back(this);
            if (fils.size()>0) 
            {
                for(int i=0; i<fils.size();i++) 
                    fils[i]->transformeEnVector(v);
            }
        }
     
        // Destructor 
        ~ arbre()
        { 
            std::vector<arbre<T>*> v;
            transformeEnVector(v);
            std::reverse(v.begin(),v.end());
            for (int i=0;i<v.size();i++)
            {   
                std::cout<<"Dans le destructeur"<<std::endl;
                std::cout<<"Iteration "<<i<<std::endl;
                delete v[i];
            }
            std::cout<<std::endl<<"Je sors du destructeur"<<std::endl;
     
        }
    Amicalement.

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

    Informations professionnelles :
    Activité : aucun

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

    Avant toute chose : pourrais tu nous donner un exemple d'utilisation de ta classe ainsi que sa définition complète(dont il semblerait que tu aies supprimé certaines fonctions, non ?)

    Ceci dit : Lorsque tu crées une variable sur la pile (comprend :sans avoir recours à l'allocation dynamique de la mémoire), le destructeur est automatiquement appelé lorsque tu sors de la portée dans laquelle cette variable est déclarée. Pour faire simple : les variables déclarées sur la pile sont automatiquement détruites (et leur destructeurs forcément appelés) lorsque tu arrive à l'accolade fermante correspondant à la fin du groupe d'instruction dans lequel la variable a été déclarée :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    void foo(){
        Type a;
        {
            Type2 b;
            /* ... */
        }    // b est détruite (et b::~Type2() est appelé automatiquement) ici
        /* ... */
    } // a est détruite (et a::~Type() est appelé automatiquement) ici
    Lorsque tu as recours à l'allocation dynamique de la mémoire, tu prend la responsabilité du moment où il faut détruire l'objet pointé par le pointeur que tu as obtenu grace à new (ou à new[]) et l'objet n'est alors détruit (et son destructeur appelé automatiquement) que lorsque tu tu invoque delete (ou delete[]) sur le pointeur en question.

    De manière générale si tu n'obtient pas l'affichage auquel tu t'attends lors du passage dans le destructeur, c'est sans doute parce que tu as eu recours à l'allocation dynamique de la mémoire pour un pointeur sur lequel tu n'as simplement jamais appelé delete

    Notes enfin que l'utilisation de pointeurs "nus" est réellement de nature à poser énormément de problèmes car il sont sources de fuites mémoire et / ou de tentative de double libération de la mémoire (tentative de libérer la mémoire allouée à un pointeur pour lequel la mémoire a déjà été libérées). Lorsque tu envisages de manipuler des pointeurs nu dans une classe ayant sémantique de valeur comme ta classe Arbre, il est important à respecter la règle des trois grands (respectivement des 5 grands, si tu envisage la sémantique de mouvement) : si tu dois fournir un comportement spécifique pour l'une des fonctions parmi le constructeur de copie, l'opérateur d'affectation ou le destructeur (ajoute le constructeur de copie par déplacement et l'opérateur d'affectation par déplacement si tu envisage la sémantique de mouvement), tu dois impérativement fournir une implémentation personnalisée de toutes les autres fonctions de cette liste

    Par contre, C++11 a "officialisé" la notion de pointeurs intelligents, qui fournissent une "enveloppe RAII" permettant de s'assurer que les ressources allouées de manière dynamique soient détruite exactement en temps opportuns. Tu serais donc très bien inspiré de les utiliser à la place de pointeurs nus
    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

  3. #3
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 627
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 627
    Points : 10 551
    Points
    10 551
    Par défaut
    Mets un point d'arrêt dans ton destructeur

    Certaines bibliothèques IHM désactive la console, et il faut utiliser la procédure OutputDebugString

    Oui, c'est moche ou ou

  4. #4
    Nouveau membre du Club
    Homme Profil pro
    Lycéen
    Inscrit en
    Octobre 2014
    Messages
    51
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Arabie Saoudite

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Octobre 2014
    Messages : 51
    Points : 35
    Points
    35
    Par défaut
    Avant toute chose : pourrais tu nous donner un exemple d'utilisation de ta classe ainsi que sa définition complète(dont il semblerait que tu aies supprimé certaines fonctions, non ?)
    Oui effectivement, j'ai supprimé quelques méthodes de ma classe. Parmi elles, il en y a qui permettent l'écriture et la lecture des attributs de arbre, d'autres qui ajoutent ou effacent un élément de fils, et enfin d'autres qui affichent de manière différentes le contenu de l'arbre. S'il le faut, je mettrai tout mon code , mais sachez que aucune de ces méthodes n'utilise une allocation dynamique avec l'opérateur new. En revanche, dans le main, je ne définis pas une instance de la classe sur la pile, mais plutôt à l'aide d'un pointeur lequel est initialisé en utilisant new. Ainsi l'accès à la racine, ainsi que tout autre noeud, de mon arbre se fait via un pointeur.

    Mon idée derrière l'implémentation du destructeur est donc que tous ces pointeurs alloués dynamiquement soient détruits lorsque le programme atteint l'accolade fermante du main().

    Voici ce que j'ai écrit dans le main :

    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
    int main(int argc, const char * argv[])
    {
        // racine
        arbre<int> * racine=new arbre<int>(100);
        // fils de racine
        racine->enfile(new arbre<int>(80)); //noeud 0
        racine->enfile(new Tree<int>(2)); 
        // fils du noeud 0
        (racine->renvoieFils(0))->enfile(new arbre<int>(4)); // noeud 1
        // fils du noeud 1
        ((racine->renvoieFils(1))->renvoieFils(0))->enfile(new arbre<int>(105));
     
        std::cout<<std::endl;
        racine->display();
        std::cout<<std::endl;
        std::cout<<root->minProfondeur()<<std::endl<<root->maxProfondeur();
     
    return 0;
    }
    Euhh, sinon, j'avoue, je n'ai pas très bien compris ta phrase koala01 qui sans doute pointe sur ce que je ne sais pas dans mon code
    De manière générale si tu n'obtient pas l'affichage auquel tu t'attends lors du passage dans le destructeur, c'est sans doute parce que tu as eu recours à l'allocation dynamique de la mémoire pour un pointeur sur lequel tu n'as simplement jamais appelé delete
    Sinon, j'ai bel et bien essayé de mettre des points d'arrêt dans mon destructeur, il n'est tout simplement jamais atteint, le programme se termine (proprement) sans que je rentre dans le mode debug !

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 611
    Points
    30 611
    Par défaut
    Citation Envoyé par souki22 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
    int main(int argc, const char * argv[])
    {
        // racine
        arbre<int> * racine=new arbre<int>(100);
        // fils de racine
        racine->enfile(new arbre<int>(80)); //noeud 0
        racine->enfile(new Tree<int>(2)); 
        // fils du noeud 0
        (racine->renvoieFils(0))->enfile(new arbre<int>(4)); // noeud 1
        // fils du noeud 1
        ((racine->renvoieFils(1))->renvoieFils(0))->enfile(new arbre<int>(105));
     
        std::cout<<std::endl;
        racine->display();
        std::cout<<std::endl;
        std::cout<<root->minProfondeur()<<std::endl<<root->maxProfondeur();
     
    return 0;
    }
    Euhh, sinon, j'avoue, je n'ai pas très bien compris ta phrase koala01 qui sans doute pointe sur ce que je ne sais pas dans mon code
    De manière générale si tu n'obtient pas l'affichage auquel tu t'attends lors du passage dans le destructeur, c'est sans doute parce que tu as eu recours à l'allocation dynamique de la mémoire pour un pointeur sur lequel tu n'as simplement jamais appelé delete
    Sinon, j'ai bel et bien essayé de mettre des points d'arrêt dans mon destructeur, il n'est tout simplement jamais atteint, le programme se termine (proprement) sans que je rentre dans le mode debug !
    Ben, justement, le code de ta fonctions main nous montre que tu as utilisé l'allocation dynamique pour créer l'ensemble des noeuds de ton arbre, y compris pour ta variable racine, vu que tu as écrit
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    arbre<int> * racine=new arbre<int>(100);
    Ce faisant, tu as décidé de prendre la responsabilité du moment où il faudra libérer les ressources allouées à cette variable (et donc, les noeud enfants par l'intermédiaire du destructeur), mais... tu n'indique jamais qu'il est temps de détruire cette variable. Il n'y a donc rien de surprenant au fait que le destructeur ne soit jamais appelé. (note au passage que, si tu n'étais pas dans la fonction main, tu aurais une belle fuite mémoire!!! Une chance que le système d'exploitation est là pour récupérer ton erreur ).

    C++ n'est pas comme java ou comme C#!!! Nous ne pouvons pas nous reposer sur un garbage collector pour libérer les ressources devenues inutiles . La règle (si on décide de ne pas utiliser les pointeurs intelligents) est qu'il faut pouvoir associer un delete à chaque new. On peut associer le delete qui se trouve dans le destructeur à l'ensemble des new qui te permettent de créer les différents noeuds enfants, mais il reste le new qui te permet de créer la variable racine qui n'est associé à aucun delete, et tous tes problèmes découlent de là

    Rajoute simplement la ligne juste avant le return 0; final, et tout rentrera dans l'ordre
    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
    Nouveau membre du Club
    Homme Profil pro
    Lycéen
    Inscrit en
    Octobre 2014
    Messages
    51
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Arabie Saoudite

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : Octobre 2014
    Messages : 51
    Points : 35
    Points
    35
    Par défaut
    J'ai rajouté delete racine juste avant le return 0.
    Quand je laisse le destructeur tel que je l'ai posté dans mon premier message, j'ai une erreur bad_access. Par contre, quand le destructeur est un bloc vide, aucune levée d'exception. Mais je ne sais pas si toute la mémoire que j'ai prise avec new est remise à mon système par mon propre main() ou si elle est gérée par une autre composante sur laquelle je n'ai pas de contrôle.

  7. #7
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Rajoute simplement la ligne juste avant le return 0; final, et tout rentrera dans l'ordre
    Je ne pense pas que ce soit la bonne direction où aller (Koala, je ne dis pas ça pour toi, je sais que tu sais, mais pour le posteur initial) : On ne doit utiliser l'allocation dynamique que quand on en a besoin, car elle est plus complexe à gérer (la preuve, tu as oublié un delete, mais ce n'est pas la seile complexité associée) et moins performante. Et si on doit l'utiliser, dans du vrai code (par opposition à un petit code de test), on l'utilise avec des mécanismes qui permettent d'en limiter les dangers (les pointeurs intelligents, mais je pense qu'il vaut mieux attendre que tu aies bien assimilé les concepts sur lesquels tu es actuellement avant d'aller regarder ce que c'est).

    En l’occurrence, je ne vois pas trop à quoi te sert l'allocation dynamique, donc plutôt que de la corriger, je te propose de la supprimer :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    arbre<int> racine(100);

    Sinon, souki22, pour ton autre problème, c'est normale que ça plante. Tu transformes ton arbre en vecteur, pour avoir une vue aplatie de ton arbre. Mais il continue à exister. Et donc, dans ton destructeur, quand tu détruit un fils, tu vas créer un nouveau vecteur aplatissant le sous arbre correspondant à ce fils, et au final, tu vas détruire plein de fois tes objets.

    Tu as plusieurs solutions pour résoudre ce problème, mais la première question à se poser est : pourquoi vouloir aplatir l'arbre ?
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  8. #8
    Membre éprouvé Avatar de fenkys
    Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Octobre 2007
    Messages
    376
    Détails du profil
    Informations personnelles :
    Âge : 56
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 376
    Points : 1 054
    Points
    1 054
    Par défaut
    Citation Envoyé par foetus Voir le message
    Oui, c'est moche ou ou
    C'est moche et pas portable.

Discussions similaires

  1. Réponses: 12
    Dernier message: 30/04/2015, 17h07
  2. Réponses: 0
    Dernier message: 07/04/2015, 19h22
  3. Réponses: 3
    Dernier message: 23/06/2011, 17h47
  4. Réponses: 2
    Dernier message: 23/12/2009, 15h41
  5. [servlet][filtre] filtre qui n'est pas appelé
    Par felix79 dans le forum Servlets/JSP
    Réponses: 4
    Dernier message: 29/06/2005, 21h09

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