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 :

"Pure virtual function called" et destructeur virtuel


Sujet :

C++

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2002
    Messages
    74
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2002
    Messages : 74
    Points : 62
    Points
    62
    Par défaut "Pure virtual function called" et destructeur virtuel
    Bonjour,

    en vue d'éradiquer un petit problème de "Pure virtual function called" à l'exécution d'un programme, j'ai mis ça dans un moteur de recherche bien connu, et je suis tombé sur une info qui m'étonne un peu :

    le destructeur des classe filles appellent naturellement le destructeur de la classe mère. le problème c'est qu'avec un Mere* pointant vers une instance de Fille, l'appel du destructeur (par delete) se contente par défaut d'appeler le destructeur de Mere (normal). il faut donc rajouter le mécanisme d'indirection virtuel pour que ça soit le destructeur du type réel de l'objet pointé qui soit appelé
    Rassurez-moi, ceci est faux ? On peut bien faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class A
    {
       A(){}   
       virtual ~A(){}
    };
     
    class B : public A
    {
       B(): A(){}
       virtual ~B(){}
    };
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void NettoyerTab()
    {
        int i =0, imax = Tab.size();
        for(i = 0; i < imax; ++i)
        {
            if(Tab[i]) != NULL)
            {
                delete ( Tab[i] );  
            }
        }
        Tab.clear();
    }

    Mon problème s'arrange en fait traquant les fonctions virtuelles pures dans les constructeurs et destructeurs.

    Je ne pense pas qu'il soit nécessaire de transtyper pour bien deleter, même dans le cas cité.
    Merci d'avance pour cette précision.
    seb

  2. #2
    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
    C'est pour le moins imprécis. Déjà, "rajouter le mécanisme d'indirection virtuel" signifie juste dans le texte déclarer le destructeur de la classe de base "virtual". Mais ce qui se passe si ce n'est pas fait, ce n'est pas simplement que le mauvais destructeur sera appelé, c'est un comportement indéfini qui peut avoir d'autres impacts (corruption mémoire...).

    Je ne vois pas de problèmes dans ton code.
    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.

  3. #3
    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,

    Visiblement, tu as mal lu ce qu'il dit:

    Si le destructeur de la classe mère n'est pas virtuel, le fait d'invoquer delete sur un pointeur vers le type de la classe mère n'appellera effectivement pas le destructeur de la classe enfant.

    Donc, ceci ne fonctionne pas:
    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 A
    {
        public:
            A(){cout<<"constructeur de A"<<endl;}
            ~A(){cout<<"destructeur de A"<<endl;}
    };
     
    class B: public A
    {
        public 
            B(){cout<<"constructeur de B"<<endl;}//s'il existe un constructeur
                                   // par défaut pour la classe mère, son appel est
                                   // implicite
            ~B(){cout<<"destructeur de B"<<endl;}
    };
    int main()
    {
        std::vector<A*> tab;
        for(int i=0;i<10;++i)
            tab.push_back(new B);
        for(int i=0;i<10;++i)
            delete tab[i];
        return 0;
    }
    Pour que le polymorphisme fonctionne, il faut que le destructeur de A soit déclaré virtuel (ce qui fera en sorte que celui de B le soit également de manière implicite, même s'il est préférable de l'indiquer explicitement )
    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

  4. #4
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2002
    Messages
    74
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2002
    Messages : 74
    Points : 62
    Points
    62
    Par défaut
    merci pour ces précisions.
    koala, tu as raison. J'aurai pu le comprendre autrement.
    Mais comme je ne vois pas de bonne raison pour ne pas avoir les destructeurs virtuels dans une hiérarchie de classes, ça m'a fait douter...
    merci
    seb

  5. #5
    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 518
    Points
    41 518
    Par défaut
    Citation Envoyé par Seb des Monts Voir le message
    Je ne vois pas de bonne raison pour ne pas avoir les destructeurs virtuels dans une hiérarchie de classes
    Tout simplement quand elles ne sont pas dédiées à un emploi polymorphique.

    GotW conseille d'avoir le destructeur de classe mère soit public et virtuel, soit protégé et non-virtuel (sachant qu'il conseille aussi de déclarer systématiquement les constructeurs de la classe mère comme protégés, et n'autoriser que l'instanciation des classes "feuilles").
    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.

  6. #6
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2002
    Messages
    74
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2002
    Messages : 74
    Points : 62
    Points
    62
    Par défaut
    ha oui !
    C'est ça d'analyser par le prisme de ses projets !
    seb

  7. #7
    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
    Je me permet une légère digression sur le sujet (on scindera si nécessaire ), mais j'en viens presque à me demander si, dans de nombreux cas, on n'en arrive pas à envisager le polymporphisme comme tout à fait naturel, alors qu'il devrait être considéré, à l'instar des goto, de la récursivité et d'autres méthode, comme ne devant intervenir que lorsque c'est réellement nécessaire...

    Comprenez moi, ce n'est pas son utilité que je remets en cause, mais son usage.

    J'ai l'impression que l'on considère le polymorphisme comme aussi naturel que le fait de respirer ou d'avoir deux jambes, sans se rendre compte qu'il s'agit d'une technique qui représente malgré tout un cout non négligeable en terme de performances.

    J'ai l'impression que, dans bien des cas, on gères des collections de pointeurs dont le type est déclaré comme étant la classe de base pour "profiter du polymorphisme", alors qu'il serait largement plus intéressant de gérer les différents types d'objets sous la forme d'autant de collections qu'il n'y a de types réels...
    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
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2002
    Messages
    74
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2002
    Messages : 74
    Points : 62
    Points
    62
    Par défaut
    naturel... quand même pas ! Utiliser le polymorphisme nécessite généralement un petit effort de conception.
    Et si on appliquait ta proposition, le code serait beaucoup moins simple et moins lisible.
    Je trouve qu'avec les performances actuelles des machines, c'est dommage de privilégier encore la performance au détriment de la lisibilité / maintenabilité du code. Jusqu'à un certain point, évidemment...
    Mais pour gérer des listes de lignes de commande, de pièces ou de services, par exemple, ce serait dommage de trop se compliquer la vie pour des petites listes d'objets relativement modestes. Le polymorphisme me semble hyper adapté pour ça, et si ça coûte un peu, c'est pas trop grave.
    Les processeurs ont été multipliés par 3 ou 4 depuis le début du projet, et les besoins du projet (en terme de gestion de listes d'objets) n'ont guère changés
    seb

  9. #9
    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 Seb des Monts Voir le message
    naturel... quand même pas ! Utiliser le polymorphisme nécessite généralement un petit effort de conception.
    Oui, bien sur qu'il nécessite généralement un effort de conception...

    Ce que je voulais dire, c'est que, bien souvent, la première idée qui vient à l'esprit lorsqu'il faut gérer une collection passe "naturellement" (dans le sens où c'est une des premières choses auxquelles on pense) passe par le polymorphisme
    Et si on appliquait ta proposition, le code serait beaucoup moins simple et moins lisible.
    j'aurais du préciser "polymorphisme dynamique"... Le bon, le dur, celui qui passe par l'héritage publique...

    Avec les template par exemple, tu obtiens également une certaine forme de polymorphisme sans forcément rendre le code ni beaucoup moins simple, ni moins lisible...

    Le tout en évitant l'écueil qui consiste à envisager l'héritage publique dans une situation dans laquelle il ne faudrait pas l'envisager en vertu du principe de subsitution de Liskov
    Je trouve qu'avec les performances actuelles des machines, c'est dommage de privilégier encore la performance au détriment de la lisibilité / maintenabilité du code. Jusqu'à un certain point, évidemment...
    Comme tu l'as dit "jusqu'à un certain point", et, "jusqu'à un certain point", je suis effectivement d'accord avec toi

    Par contre, je ne suis vraiment pas intimement persuadé qu'il soit possible de faire un parallèle entre l'utilisation du polymorphisme et la lisibilité / maintenabilité du code.

    Pour certains langages uni paradigmes, le parallèle est plus facile à faire, mais, en C++, la possibilité d'utiliser le paradigme de programmation générique rend ce parallèle beaucoup plus hasardeux
    Mais pour gérer des listes de lignes de commande, de pièces ou de services, par exemple, ce serait dommage de trop se compliquer la vie pour des petites listes d'objets relativement modestes. Le polymorphisme me semble hyper adapté pour ça, et si ça coûte un peu, c'est pas trop grave.
    Les processeurs ont été multipliés par 3 ou 4 depuis le début du projet, et les besoins du projet (en terme de gestion de listes d'objets) n'ont guère changés
    Je me suis peut être mal exprimé, mais il y a une mauvaise compréhension de ce que je voulais dire...

    Bien sur, je le répète, le polymorphisme "dynamique" est nécessaire / utile / intéressant.

    Bien sur, si tu veux gérer en même temps des camions et des voitures, il faut les gérer comme étant de leur base commune qui est véhicule, je ne disconvient absolument pas de ce fait.

    Par contre, là où il y a un problème, c'est quand, alors que l'on sait pertinemment que l'on va gérer une collection de "Voiture", on travaille avec une collection de "Vehicule* ", que Voiture et Vehicule soient effectivement polymorphes ou non, avec des transtypages dans tous les coins parce que l'on a, décidément, besoin de fonctions qui sont propres au type Voiture...

    Dans de telles circonstances, nous flirtons très sérieusement avec le problème de conception
    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

  10. #10
    screetch
    Invité(e)
    Par défaut
    un cas typique d'emploi abusif de l'héritage : les bibliotheques de widget graphiques a 22 étages

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

Discussions similaires

  1. Réponses: 9
    Dernier message: 02/04/2008, 11h05
  2. [pure virtual call] erreur d'execution
    Par ZaaN dans le forum C++
    Réponses: 9
    Dernier message: 22/02/2008, 18h32
  3. Réponses: 2
    Dernier message: 14/06/2007, 11h05
  4. Réponses: 4
    Dernier message: 17/05/2007, 17h47
  5. performances des virtual functions
    Par xxiemeciel dans le forum C++
    Réponses: 2
    Dernier message: 25/07/2005, 18h24

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