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

Langage C++ Discussion :

constructeurs multiples - héritage


Sujet :

Langage C++

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Mars 2006
    Messages
    19
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Mars 2006
    Messages : 19
    Par défaut constructeurs multiples - héritage
    Bonjour,

    Voilà mon contexte :

    Une classe A ayant pour constructeur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    A::A(int a) { this->a = a }
    Une classe B héritant de A ayant deux constructeurs :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    B::B() { B(0) } // appel du constructeur avec un paramètre entier
    B::B(int a) : A(a) { } // appel au constructeur de la classe parent
    Comme vous avez pu le constater ce que j'aimerais c'est que si j'instancie mon objet B sans paramètre, celui-ci appel le constructeur avec un paramètre (0 en l'occurance).
    Donc ça ça joue bien, pas de problèmes, mais j'aimerais évidemment que ma classe B appel le constructeur A lors de l'instanciation.

    Et de la manière dans laquelle je l'ai présenté ci-dessus ça ne marche pas. Compilation + Linking ok, mais erreur de segmentation lors de l'exécution.

    Mon main ressemble donc à celà :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    int main(void) {
    A* objet = new B();
    }
    Merci d'avance de votre aide future.

  2. #2
    Membre expérimenté
    Homme Profil pro
    Analyse système
    Inscrit en
    Novembre 2008
    Messages
    227
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Analyse système
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Novembre 2008
    Messages : 227
    Par défaut
    Le plus simple est de déclaré un constructeur dans B avec paramètre optionnel :

  3. #3
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par Rocknacro Voir le message
    Bonjour,

    Voilà mon contexte :

    Une classe A ayant pour constructeur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    A::A(int a) { this->a = a }
    Très mauvaise idée...

    Il vaut toujours mieux utiliser les listes d'initialisation

    Une classe B héritant de A ayant deux constructeurs :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    B::B() { B(0) } // appel du constructeur avec un paramètre entier
    B::B(int a) : A(a) { } // appel au constructeur de la classe parent
    On n'appelle JAMAIS le constructeur d'une classe dans un autre constructeur de la même classe !!!!

    Par contre, comme c'est le constructeur de A qui t'intéresse, tu peux invoquer explicitement celui-ci dans la liste d'initialisation, en lui passant les valeurs ad-hoc:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    //pour le constructeur par défaut de B
    B::B():A(0){}
    // pour le constructeur de B prenant un int
    B::B(int i) : A(i){}
    Donc ça ça joue bien, pas de problèmes, mais j'aimerais évidemment que ma classe B appel le constructeur A lors de l'instanciation.
    Tu ne peux pas l'éviter, vu que B hérite de A et donc que B EST UN A...

    Comme la base de B est A, il faut absolument s'asusrer que la partie de B qui vient de A soit correctement initialisée, sinon, tu va avoir des problèmes
    Et de la manière dans laquelle je l'ai présenté ci-dessus ça ne marche pas. Compilation + Linking ok, mais erreur de segmentation lors de l'exécution.
    Et pour cause...
    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
    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
    Par défaut
    Bonjour,
    En C++0x, tu peux enchaîner les constructeurs comme suit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    struct A
    {
       A(int a_):a(a_){}
    private:
       int a;
    };
    struct B : public A
    {
       B():B(0){}
       B(int a):A(a){}
    };
    Mais mieux, en C++0x tu peux aussi hériter des constructeurs des classes de bases :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    struct A
    {
       A(int a_):a(a_){}
    private:
       int a;
    };
    struct B : public A
    {
       using A::A; // et hop, tu as naturellement un constructeur B(int) qui appelle directement A(int) !!!
    };
    LE hic ? C'est qu'aujourd'hui la délégation et l'héritage de constructeur ne sont pas encore disponibles sur les compilateurs Visual ou GCC

    Et en C++98, ce n'est pas possible d'enchaîner les constructeurs ou d'en hériter. Il te faut donc soit passer par une fonction d'initialisation dédiée si le nombre de paramètres et le contenu des constructeurs étaient complexe, soit simplement écrire à la main :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    B::B():A(0) { } // appel du constructeur avec un paramètre entier
    B::B(int a) : A(a) { }
    P.S. : Pourquoi une allocation dynamique ? Réflexe Java/C# ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    int main(void)
    {
       B b;
       A& objet = b;
     
     
       return 0;
    }

  5. #5
    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
    Par défaut
    Citation Envoyé par AF_2.8 Voir le message
    Le plus simple est de déclaré un constructeur dans B avec paramètre optionnel :
    Je n'ai jamais beaucoup aimé des paramètres optionnels mais c'est probablement une question de goût.
    Bref, tout ceci pour dire qu'en lisant cette réponse cela m'a fait penser que si ton constructeur n'a qu'un seul paramètre (optionnel ou pas) pense à le déclarer explicit pour éviter des comportements inattendus :
    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
    struct A
    {
        protected:
       A(int a_):a(a_){}
       ~A(){}
     
    private:
       int a;
    };
     
    struct B : public A
    {
       B(int a_=0):A(a_){}
    };
     
    struct C : public A
    {
       explicit C(int a_=0):A(a_){}
    };
     
     
    void fonction1(B){}
    void fonction2(C){}
    int main()
    {
        fonction1(1);  // convertit 1 en B(1). Est-ce vraiment voulu ???
        fonction2(1); // erreur
        fonction2(static_cast<C>(1));
        return 1;
    }

  6. #6
    Membre averti
    Profil pro
    Inscrit en
    Mars 2006
    Messages
    19
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Mars 2006
    Messages : 19
    Par défaut
    Ce que je n'ai pas précisé, c'est que dans mon constructeur "principal" je fais beaucoup d'autres opérations.

    En faisant
    Cela me permettait de placer toutes mes instructions uniquement dans le constructeur avec paramètre B::B(int a).

    Sinon oui effectivement, l'allocation dynamique est un réflexe Java... La manière présenté ci-dessus y est d'ailleurs assez courante en Java.

    EDIT: pour le constructeur explicite, c'est justement l'implémentation du polymorphisme qui m'intéresse ici, mais merci de l'info

  7. #7
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par Rocknacro Voir le message
    Ce que je n'ai pas précisé, c'est que dans mon constructeur "principal" je fais beaucoup d'autres opérations.

    En faisant
    Cela me permettait de placer toutes mes instructions uniquement dans le constructeur avec paramètre B::B(int a).

    Sinon oui effectivement, l'allocation dynamique est un réflexe Java... La manière présenté ci-dessus y est d'ailleurs assez courante en Java.
    Dans ce cas, tu factorise ton code dans une fonction qui va faire les autres opérations, et tu invoque cette fonction dans ton constructeur:

    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
    class B : public A
    {
         public:
             B():A(0)
             {
                 doManyOtherThinks();
             }
             B(int i):A(i)
             {
                 doManyOtherThinks();
             }
        private:
            void doManyOtherThinks()
            {
                // what you want
            }
    };
    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
    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
    Par défaut
    Citation Envoyé par Rocknacro Voir le message
    EDIT: pour le constructeur explicite, c'est justement l'implémentation du polymorphisme qui m'intéresse ici, mais merci de l'info
    explicit c'est par rapport au polymorphisme de coercition qui peut avoir sur int pas par rapport au polymorphisme d'inclusion lié à l'héritage.

  9. #9
    Membre averti
    Profil pro
    Inscrit en
    Mars 2006
    Messages
    19
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Mars 2006
    Messages : 19
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Dans ce cas, tu factorise ton code dans une fonction qui va faire les autres opérations, et tu invoque cette fonction dans ton constructeur:
    Ok c'est fait, ça me plaît pas trop j'aurais préféré tout faire dans un seul constructeur (refactoring ou pas) mais ça marche comme ça.

    Merci à tous pour vos remarques intéressantes.

  10. #10
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par Rocknacro Voir le message
    Ok c'est fait, ça me plaît pas trop j'aurais préféré tout faire dans un seul constructeur (refactoring ou pas) mais ça marche comme ça.

    Merci à tous pour vos remarques intéressantes.
    Pourquoi donc est-ce que cela ne te plait pas trop

    Une saine pratique à veiller à mettre en oeuvre est de veiller à ne pas se répéter soi-même: si tu dois écrire un code plus d'une fois, c'est (souvent) que tu dois l'écrire trop souvent et donc qu'il est intéressant de créer une fonction qui s'occupera exclusivement de cette tâche.

    Comme on ne peut (du moins avec la norme en vigueur, car C++11x n'est pas encore ni finalisé, ni complètement supporté), pas appeler un constructeur depuis un autre constructeur de la même classe, on n'a pas vraiment d'autre choix

    Tu dois simplement faire attention au fait que tu ne peux pas espérer invoquer un comportement polymorphe, car le type (dynamique) d'un objet au moment du constructeur est... le type pour lequel le constructeur est invoqué .
    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

  11. #11
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    LE hic ? C'est qu'aujourd'hui la délégation et l'héritage de constructeur ne sont pas encore disponibles sur les compilateurs Visual ou GCC
    Pour info, la délégation de constructeur est présent sous clang depuis quelques jours.

  12. #12
    Membre averti
    Profil pro
    Inscrit en
    Mars 2006
    Messages
    19
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Mars 2006
    Messages : 19
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Comme on ne peut (du moins avec la norme en vigueur, car C++11x n'est pas encore ni finalisé, ni complètement supporté), pas appeler un constructeur depuis un autre constructeur de la même classe, on n'a pas vraiment d'autre choix
    ... Voilà pourquoi ça ne me "plait pas trop", car je suis habitué au monde Java. Mais effectivement la méthode ainsi appliquée, diviser pour régner, résout très bien le problème et enlève toute redondance à mon code.

  13. #13
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par Rocknacro Voir le message
    ... Voilà pourquoi ça ne me "plait pas trop", car je suis habitué au monde Java. Mais effectivement la méthode ainsi appliquée, diviser pour régner, résout très bien le problème et enlève toute redondance à mon code.
    Primo, nous ne sommes pas sous java, mais sous C++, et, secundo, pose toi peut être simplement la question de savoir ce qu'est un constructeur...:

    Ce n'est jamais qu'une fonction membre (comme n'importe quelle autre) qui a une responsabilité clairement définie : faire en sorte de fournir un objet correctement initialisé au moment où l'on a besoin de l'objet.

    A partir de là, je ne vois pas ce qu'il y a de mal à, tout comme dans n'importe quelle autre fonction, décider d'invoquer une fonction qui prend en charge une partie (clairement définie) de ce que l'on en attend

    Il faut juste faire attention aux éventuels problèms liés à la virtualité des fonctions appelées, car, dans tous les cas, le type envisagé lors de l'appel d'une éventuelle fonction virtuelle ne sera que celui du type en cours de construction (c'est à dire de la classe mère et non de la classe dérivée si l'appel de la fonction se fait dans le constructeur de la classe mère )
    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

  14. #14
    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 : 46
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Par défaut
    Citation Envoyé par koala01 Voir le message
    A partir de là, je ne vois pas ce qu'il y a de mal à, tout comme dans n'importe quelle autre fonction, décider d'invoquer une fonction qui prend en charge une partie (clairement définie) de ce que l'on en attend
    De mal, absolument rien.

    Mais il faut admettre que lorsque cette fameuse fonction correspond à un des constructeurs, c'est juste pénible de devoir la faire. En ce sens la délégation de constructeur n'est certes pas quelque chose d'indispensable mais c'est un sucre malgré tout bien sympathique.

  15. #15
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par gl Voir le message
    De mal, absolument rien.

    Mais il faut admettre que lorsque cette fameuse fonction correspond à un des constructeurs, c'est juste pénible de devoir la faire. En ce sens la délégation de constructeur n'est certes pas quelque chose d'indispensable mais c'est un sucre malgré tout bien sympathique.
    Je n'ai jamais dit que ce n'était pas sympathique, j'ai juste dit que... c'est un sucre, tout sympathique soit il

    Ma remarque ne portait pas sur le "est-ce sympa de pouvoir déléguer le constructeur" mais plutôt sur ce que j'ai ressenti comme une envie réellement profonde de ne pas simplement déléguer la tâche à une fonction séparée
    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

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

Discussions similaires

  1. Syntaxe : constructeur multiple
    Par Teo dans le forum Langage
    Réponses: 6
    Dernier message: 28/05/2008, 04h55
  2. Bonnes pratiques des constructeurs multiples
    Par TheDrev dans le forum C++
    Réponses: 6
    Dernier message: 05/05/2008, 19h06
  3. interet des constructeurs multiples
    Par pseudobidon57 dans le forum Langage
    Réponses: 2
    Dernier message: 13/06/2007, 12h33
  4. Constructeur et héritage.
    Par Pragmateek dans le forum C++
    Réponses: 3
    Dernier message: 15/08/2006, 21h01
  5. [POO] Constructeur et héritage
    Par LDDL dans le forum Langage
    Réponses: 3
    Dernier message: 23/05/2006, 21h44

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