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 :

Constructeur destructeur et heritage


Sujet :

C++

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Décembre 2006
    Messages
    209
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Décembre 2006
    Messages : 209
    Points : 83
    Points
    83
    Par défaut Constructeur destructeur et heritage
    Bonjour je suis un debutant en C++ j'essaye de revoir mes bases j'ai volu tester le cas suivant
    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
    #include <iostream>
    using namespace std;
    class A
    {
     
    public:
        A(){ cout << "A::const\n"; };
        ~A(){cout<< "destruc A \n";};
    void F1() { cout << "A::F1()\n"; }
    virtual void F2() { cout << "A::F2()\n"; }
    };
     
    class B : public A
    {
     
    public:
        B() { cout << "B::const\n"; }
        ~B(){cout<< "destruc B \n";};
    void F1() { cout << "B::F1()\n"; }
    void F2() { cout << "B::F2()\n"; }
    };
    dans ma fonction main je crée un objet du type b comme ceci B b; j'ai la sortie suivante
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    A::const
    B::const
    destruc B 
    destruc A
    j'aimerai savoir pourquoi j'ai un appel de constructeur et destructeur de la classe A sachant que j'ai crée l'objet avec le constructeur de la classe B ?
    est ce qu'il y a un appel implicite des fonction constructeur/destructeur de la classe A ? y a t il un moyen de l'eviter ?

    et si j'ajoute à mon main()
    j'obtiens
    merci
    merci

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 630
    Points : 30 699
    Points
    30 699
    Par défaut
    Salut,
    Citation Envoyé par yassinegoth Voir le message
    Bonjour je suis un debutant en C++ j'essaye de revoir mes bases j'ai volu tester le cas suivant
    Tout le monde a été un jour débuttant
    j'aimerai savoir pourquoi j'ai un appel de constructeur et destructeur de la classe A sachant que j'ai crée l'objet avec le constructeur de la classe B ?
    La raison est simple: l'héritage est une relation "EST-UN", dans le sens le plus sémantique du terme que l'on puisse trouver.

    L'objet dérivé est donc strictement composé de ce dont est composé le type dérivé

    Cela impllique que, pour arriver à construire (respectivement à détruire) un objet de type B, il faut, d'abord et avant tout avoir... un objet de type A correctement construit, autrement, les fondations même de ton objet de type B ne seraient pas stables
    est ce qu'il y a un appel implicite des fonction constructeur/destructeur de la classe A ?
    oui, parce que tu ne peux pas avoir un objet du type dérivé si tu n'a pas à la base un objet du type de base correctement construit


    L'ordre de construction est donc:
    1. construction des membres propres au type de base, dans l'ordre de leur délcaration
    2. exécution du constructeur du type de base (des fonctions qu'il contient)
    3. construction des membres propres au type dérivé, dans l'ordre de leur déclaration
    4. exécution du constructeur du type dérivé
    et l'ordre de destruction est strictement inversé, à savoir:
    1. exécution du destructeur du type dérivé
    2. destruction des membres propres au type dérivé, dans l'ordre inverse de leur déclaration
    3. exécution du destructeur du type de base
    4. destruction des membres propres au type dérivé, dans l'ordre inverse de leur délcaration
    y a t il un moyen de l'eviter ?
    Non, pas sans renoncer à l'héritage... Et c'est encore heureux
    et si j'ajoute à mon main()
    j'obtiens
    merci
    merci
    Il est déjà surprenant que cela compile, car, le code
    déclare en réalité une variable de type a qui devrait être initialisée par le retour d'une fonction appelée B() (qui n'existe pas) et dont le code serait sans doute proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    A B()
    {
         A var;
        /* n'importe quoi */
        return var;
    }
    Nota: Enfin, tu fais un certain nombre d'erreurs pourraient s'avérer désastreuses:
    - Le destructeur de ton type de base DOIT IMPERATIVEMENT être déclaré virtuel, car, autrement, lorsque tu aura un objet dont le type réel (dynamique) est le type dérivé mais "passant pour être du type de base" et qu'il sera temps de le détruire, seul le destructeur du type de base sera invoqué, et cela provoquera forcément des problèmes

    - Seuls les pointeurs et les références permettent de faire passer un objet du type dérivé comme "étant du type de base". (bref, d'utiliser le polymorphisme)

    En effet, la déclaration d'une variable "normale" vaut définition et donc appel implicite du constructeur (du type indiqué) ad-hoc

    Les deux seules possibilités pour avoir effectivement un comportement polymorphe sont:
    La définition d'une référence (du type de base) sur un objet du type dérivé existant, donc quelque chose proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    B monB;
    A & refSurB = monB;
     
    refSurB.f2(); // B::f1 est appelé
    L'affectation de l'adresse mémoire à laquelle se trouve un objet de type dérivé (créé de manière statique ou dynamique) à un pointeur du type de base, donc sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    B monBStatique;
    A * prtStatique = & monBStatique;
    /* OU OU OU OU */
    A * ptrNew = new B;
    /* ne pas oublier de libérer la mémoire allouée à ptrNew lorsqu'on n'en
     * a plus besoin ;-)
     */
    delete ptrNew;
    - Seules les fonctions déclarée virtuelles seront susceptibles de fournir un comportement polymorphe, ainsi, sur base de tes deux classes, on aurait:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    B monB;
    monB.f1() ; // appel de B::f1() ( normal ;-) )
    monB.f2() ; // appel de B::f2() ( normal ;-) )
    A monA;
    monA.f1() ; // appel de A::f1() ( normal ;-) )
    monA.f2() ; // appel de A::f2() ( normal ;-) )
    A & refSurB = monB;
    refSurB .f1() ; // appel de A::f1() ... OUPPPSSS
    refSurB .f2() ; // appel de A::f2() ... c'est ce qu'on espère
    A copieDeB = monB; // interdit

  3. #3
    Membre éclairé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2010
    Messages
    434
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2010
    Messages : 434
    Points : 654
    Points
    654
    Par défaut
    Bonjour

    Bah ta classe B dépend de ta classe A donc c'est normal que le constructeur soit appelé.

    Maintenant dans le constructeur de B tu peux faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
     B(var t) : A(t) {...}
    Par exemple pour passer des paramètres a ta classe mère comme ca tu n'a pas a initialisé les paramètres de ta classe A dans la classe B

    Ca évite la redondance de code la classe A s initialise très bien toute seule.

    Aprés va voir du coté des heritage virtuel.

    Bonne journée

  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
    Salut,

    Citation Envoyé par koala01 Voir le message
    Il est déjà surprenant que cela compile
    Euh, pourquoi. Ce code est légal (mais ne fait probablement pas ce que le PO attend). Cela créé une variable de type A initialisée par copie d'une variable de type B. Certes tout ce qui est spécifique à B est perdu mais ce qui concerne A est correctement initialisée.

    Pour l'ordre de construction/destruction, cf F.A.Q. : Dans quel ordre sont construits les différents composants d'une classe ?

    Sur les différents type d'héritage : Quand dois-je faire un héritage public ? protégé ? privé ?

    Sur le typage dynamique/statique d'une variable : Type statique et type dynamique

  5. #5
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Citation Envoyé par koala01 Voir le message
    - Le destructeur de ton type de base DOIT IMPERATIVEMENT être déclaré virtuel, car, autrement, lorsque tu aura un objet dont le type réel (dynamique) est le type dérivé mais "passant pour être du type de base" et qu'il sera temps de le détruire, seul le destructeur du type de base sera invoqué, et cela provoquera forcément des problèmes
    Non, pas forcément. Pas dans le cas présent, en tout cas.
    Pour sa compréhension, il vaut mieux être clair sur les choses.

    Sa classe ne gère aucune ressource. Il n'y a donc aucun risque.

  6. #6
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Points : 4 637
    Points
    4 637
    Par défaut
    Citation Envoyé par oodini Voir le message
    Non, pas forcément. Pas dans le cas présent, en tout cas.
    Pour sa compréhension, il vaut mieux être clair sur les choses.

    Sa classe ne gère aucune ressource. Il n'y a donc aucun risque.
    Euh, si ma mémoire est bonne, appeler polymorphiquement un destructeur non virtuel est un undefined behavior même si la classe ne gère aucune ressource.
    Si tel est bien le cas, c'est donc bien une erreur et même si en pratique ça fonctionne bien avec certains compilateurs, cela pourrait avoir des effets étranges avec d'autres.

  7. #7
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Citation Envoyé par gl Voir le message
    Euh, si ma mémoire est bonne, appeler polymorphiquement un destructeur non virtuel est un undefined behavior même si la classe ne gère aucune ressource.
    Si tel est bien le cas, c'est donc bien une erreur et même si en pratique ça fonctionne bien avec certains compilateurs, cela pourrait avoir des effets étranges avec d'autres.
    J’ai relu rapidement, je n’ai rien vu de tel dans la norme. Ça fait de la merde la plupart du temps, oui, mais je pense que le comportement est défini (appeler le destructeur demandé statiquement).

    Par contre, il est assez clair que par défaut, un destructeur devrait être virtuel. Les cas où ce n’est pas souhaitable sont tellement rares que ça ne me choque pas d’apprendre aux débutants qu’un destructeur doit impérativement être virtuel.

  8. #8
    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 : 50
    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 Le standard 5.3.5/3
    In the first alternative (delete object), if the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined. In the second alternative (delete array) if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 630
    Points : 30 699
    Points
    30 699
    Par défaut
    Citation Envoyé par white_tentacle Voir le message
    J’ai relu rapidement, je n’ai rien vu de tel dans la norme. Ça fait de la merde la plupart du temps, oui, mais je pense que le comportement est défini (appeler le destructeur demandé statiquement).

    Par contre, il est assez clair que par défaut, un destructeur devrait être virtuel. Les cas où ce n’est pas souhaitable sont tellement rares que ça ne me choque pas d’apprendre aux débutants qu’un destructeur doit impérativement être virtuel.
    Du moins, pour les destructeurs ayant des visées polymorphes, nous sommes d'accords

    Mais, quand bien même la classe de base ne gère aucune ressource, nous ne pouvons absolument pas avoir la certitude qu'il en ira de même pour les classes dérivées par l'utilisateurs.

    Si ta classe de base a vocation à être dérivée (par toi ou par l'utilisateur), tu ne peux donc pas te baser sur le fait que ta classe de base ne gère aucune ressource pour décider de ne pas rendre le destructeur virtuel, simplement parce que c'est une décision qui ne t'appartient pas dans le cas de la classe de base

  10. #10
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    JolyLoic : Ce que tu cites s'applique au cas général. Si on regroupe les cas où il y a des ressources à gérer ou de l'héritage, effectivement, le comportement est indéfini. Mais les bouquins C++ rentrent dans le détail, et nous expliquent ce qui se passe selon les cas.

    Citation Envoyé par koala01 Voir le message
    Mais, quand bien même la classe de base ne gère aucune ressource, nous ne pouvons absolument pas avoir la certitude qu'il en ira de même pour les classes dérivées par l’utilisateur.
    Si nous interdisons à cette classe d'être dérivée (constructeur privé + une fonction membre retournant un objet de cette classe), si.

    Citation Envoyé par koala01 Voir le message
    Si ta classe de base a vocation à être dérivée (par toi ou par l'utilisateur)
    Ce qui fait une condition, conviens-en.

  11. #11
    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 : 50
    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 oodini Voir le message
    JolyLoic : Ce que tu cites s'applique au cas général. Si on regroupe les cas où il y a des ressources à gérer ou de l'héritage, effectivement, le comportement est indéfini. Mais les bouquins C++ rentrent dans le détail, et nous explique ce qui se passe selon les cas.
    Si un bouquin dit que sur tel compilateur, dans telle version, le comportement est défini de telle façon, Ok. Si le bouquin dit qu'une des raisons de rendre le comportement indéterminé, c'est parce que dans tel ou tel cas, la situation était difficile, OK.

    Mais un bouquin dit autre chose que ce que j'ai cité, ce n'est pas qu'il entre dans les détails, c'est qu'il se plante, tout simplement.

  12. #12
    Membre régulier
    Profil pro
    Inscrit en
    Décembre 2006
    Messages
    209
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Décembre 2006
    Messages : 209
    Points : 83
    Points
    83
    Par défaut
    Merci je savais pas que ma question aura tant de réponses

  13. #13
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    oodini: Tu as un nom de bouquin comme exemple ? Je viens de vérifier dans ceux que j'ai qui aborde ce point (EffC++ et MC++D), dans les deux cas ils ne détaillent même pas : c'est un UB, il ne faut pas le faire. (EffC++ 3° Ed page 40, MC++D page 12).

    Et dans les détails ou pas ca reste toujours un UB, ca ne l'est peut-etre pas pour une plateforme particulière et/ou tel compilateur, mais du coup ton code n'est plus portable.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 630
    Points : 30 699
    Points
    30 699
    Par défaut
    Citation Envoyé par oodini Voir le message
    Si nous interdisons à cette classe d'être dérivée (constructeur privé + une fonction membre retournant un objet de cette classe), si.


    Ce qui fait une condition, conviens-en.
    Si ce n'est que nous sommes clairement dans le cadre d'un héritage avec un destructeur public...

    Les conditions sont donc parfaitement réunies

    Pour être précis, dés qu'il y a héritage, il y a deux cas à prendre en compte:
    1. possibilité de dériver, mais impossibilité de détruire un objet du type dérivé en le faisant passer pour un objet du type de base : destructeur protégé, non virtuel
    2. Possibilité de dériver, nécessité (ou souhait) de permettre la destruction des objets (quel que soit leur type réel) en les faisant passer comme "étant du type de base" : destructeur public et virtuel
    Et, dans le cas exposé par yassinegoth (qui sera d'ailleurs le cas le plus couremment envisagé lorsqu'il est question d'héritage), il est clair que, quoi qu'il arrive, il y a une erreur quelque part et que la solution la plus sensée est d'envisager le destructeur public et virtuel

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

Discussions similaires

  1. Réponses: 5
    Dernier message: 03/11/2008, 20h27
  2. [Débutant] Constructeur ~Destructeur
    Par grabriel dans le forum C++
    Réponses: 9
    Dernier message: 19/05/2008, 19h14
  3. aide pour constructeur destructeur
    Par aliwatte dans le forum C++
    Réponses: 4
    Dernier message: 13/03/2008, 23h23
  4. Constructeur & Destructeur en UML
    Par fredhali2000 dans le forum UML
    Réponses: 5
    Dernier message: 31/03/2006, 20h31
  5. [D7] constructeur / destructeur
    Par raoulmania dans le forum Langage
    Réponses: 2
    Dernier message: 16/12/2005, 19h00

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