Publicité
+ Répondre à la discussion
Affichage des résultats 1 à 10 sur 10
  1. #1
    Invité de passage
    Homme Profil pro
    Étudiant
    Inscrit en
    décembre 2012
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : décembre 2012
    Messages : 3
    Points : 0
    Points
    0

    Par défaut Appel récursif au constructeur : double erreur

    Bonjour,

    J'ai commencé le C++ il y a peu et j'ai rencontré un petit problème en essayant d'appeler un constructeur en récursif :

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class Sinus {
     
        int MU;
        public:
        Sinus(){MU=1;}
        Sinus(int mu){
     
            if(mu==0){
                int m;
                cout<<"Autre nombre"<<endl;
                cin>>m;
                Sinus(m);
     
                }
            else{
                MU = mu;
                }
            }
    };
    Ce code n'a même pas compilé, j'ai donc modifié le code comme ceci :

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class Sinus {
    
        int MU;
        public:
        Sinus(){MU=1;}
        Sinus(int mu){
            int m;
            if(mu==0){
                cout<<"Autre nombre"<<endl;
                cin>>m;
                Sinus(m);
    
                }
            else{
                MU = mu;
                }
            }
    };
    Ce programme compile sans problème, mais si l'utilisateur entre 0, je récupère un mauvais MU (MU = 200895864).
    J'ai donc deux questions :
    -Pourquoi le 1e code ne compile pas ?
    -Pourquoi le second me renvoie un mauvais MU ?

    Merci d'avance !

  2. #2
    Expert Confirmé Sénior

    Homme Profil pro Pierre
    Ingénieur développement logiciels
    Inscrit en
    juin 2007
    Messages
    2 182
    Détails du profil
    Informations personnelles :
    Nom : Homme Pierre
    Localisation : France

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

    Informations forums :
    Inscription : juin 2007
    Messages : 2 182
    Points : 5 060
    Points
    5 060

    Par défaut

    Un constructeur n'est jamais récursif.

    L'appel d'un constructeur crée une valeur de ce type, même depuis le code d'un autre constructeur.

    Pour initier une valeur pendant la construction, la syntaxe est:
    Code :
    1
    2
    3
    4
    5
    6
    class s {
    private:
        int m;
    public:
        s(int value) : m(value) {}
    };
    Pour modifier la valeur une fois l'objet construit, on utilise une affectation classique.
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class s {
    private:
        int* m;
    public:
        s(int value) : m(std::nullptr) {
            m = new int*;
            *m = value;
        }
    };
    Donc pour moi, ta classe devrait être
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class Sinus {
    private:
        int MU;
    public:
        Sinus():MU(1){}
     
        Sinus(int mu): MU(mu){
            while (mu==0){
                cout <<"Autre nombre"<<endl;
                cin>>mu;
            }
        }
    };
    voire même, par le truchement des arguments par défaut et de la gestion d'exception:
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    class Sinus {
    private:
        int MU;
    public:
        Sinus(int mu = 1) : MU(mu) {
            if (MU==0) throw std::invalid_argument("Sinus(0) is invalid");
        }
    };
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • La plus sotte des questions est celle qu'on ne pose pas.

    Pour faire des graphes, essayez yEd.

  3. #3
    Invité de passage
    Homme Profil pro
    Étudiant
    Inscrit en
    décembre 2012
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : décembre 2012
    Messages : 3
    Points : 0
    Points
    0

    Par défaut

    Merci pour ta réponse !

    Mon professeur m'a affirmé, pendant que je postais ce code sur le forum, qu'un appel à un constructeur en récursif était possible car c++ ne créait ainsi qu'un seul objet (il m'en a d'ailleurs fait plusieurs exemples concluants).

    Que se passe t-il lorsque l'on appelle un constructeur récursivement ? Y-a t-il des conditions à respecter si l'on veut appeler un constructeur en récursif ou est-ce à bannir définitivement ?

  4. #4
    Expert Confirmé Sénior


    Homme Profil pro Denis
    Étudiant
    Inscrit en
    décembre 2011
    Messages
    5 004
    Détails du profil
    Informations personnelles :
    Nom : Homme Denis
    Âge : 21
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : Enseignement

    Informations forums :
    Inscription : décembre 2011
    Messages : 5 004
    Points : 14 900
    Points
    14 900

    Par défaut

    Citation Envoyé par Achamian Voir le message
    Mon professeur m'a affirmé, pendant que je postais ce code sur le forum, qu'un appel à un constructeur en récursif était possible car c++ ne créait ainsi qu'un seul objet (il m'en a d'ailleurs fait plusieurs exemples concluants).
    J'ai un gros doute là-dessus.
    Peux-tu nous montrer vos "exemples" ?

    Si tu fait :
    Code :
    1
    2
    3
    4
    5
    C::C(int i)
    {
             if(i)
               C(--i);
    }
    Tu va créer des objets temporaires qui seront détruits dès la fin de la ligne et je ne vois pas trop l'intérêt de faire ça à par tenter de rendre le code plus obscur.
    Si on veut créer plusieurs instances de C, on utilisera une factory.

  5. #5
    Expert Confirmé Sénior

    Homme Profil pro Pierre
    Ingénieur développement logiciels
    Inscrit en
    juin 2007
    Messages
    2 182
    Détails du profil
    Informations personnelles :
    Nom : Homme Pierre
    Localisation : France

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

    Informations forums :
    Inscription : juin 2007
    Messages : 2 182
    Points : 5 060
    Points
    5 060

    Par défaut

    Regarde toute la section constructeurs de la faq.

    Il faut comprendre qu'un constructeur est constitué de plusieurs choses très distinctes, et que ce qu'on appellerai "constructeur récursif" est forcément impossible pour cette raison même.

    Supposons la classe suivante:
    Code :
    1
    2
    3
    4
    class s {
    private:
        int m;
    };
    Le constructeur aura la forme suivante: signature : construction {initialisation} c'est à dire S::s(int value) : m(value) {}La partie construction est un ensemble d'appels aux constructeurs de chaque membre, classe mère et classe virtuelle mère.
    Les types de bases utilisent la même notation, par exemple: m(2)Chaque membre ne peut être construit qu'une fois, ils sont construit dans l'ordre d'instanciation par la classe. (plus d'informations)
    Les membres non explicitement construits utilisent leur constructeur par défaut (pointeur et type de bases utilisent en théorie 0, mais mieux vaut les spécifier).

    La partie initialisation est appelée une fois l'objet construit. Elle permet de modifier l'objet pour le placer dans un état valide.
    Le constructeur ne peut pas appeler un constructeur récursivement, parce quand on voudrait le faire, l'objet est déjà construit, et on est en train de l'initialiser.

    Par contre, il existe des arguments par défaut, qui permettent de regrouper plusieurs constructeurs en un seul.

    Dans tous les cas, un appel au constructeur qui ne parvient pas à se terminer à cause d'une exception (d'où quelle vienne) provoque la désallocation immédiate de l'objet.
    Le destructeur de l'objet n'est pas appelé (puisqu'il n'est pas pleinement construit), par contre tout membre construit au moment de l'exception est bien détruit.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • La plus sotte des questions est celle qu'on ne pose pas.

    Pour faire des graphes, essayez yEd.

  6. #6
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro Loïc Joly
    Développeur informatique
    Inscrit en
    août 2004
    Messages
    4 982
    Détails du profil
    Informations personnelles :
    Nom : Homme Loïc Joly
    Âge : 40
    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 : 4 982
    Points : 12 136
    Points
    12 136

    Par défaut

    A noter qu'il est possible en C++11 qu'un constructeur en appelle un autre (c'est connu sous le nom de delegating constructors), mais ça ne permet en aucun cas des appels récursifs.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Et celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++

  7. #7
    Membre régulier
    Inscrit en
    juin 2008
    Messages
    128
    Détails du profil
    Informations forums :
    Inscription : juin 2008
    Messages : 128
    Points : 78
    Points
    78

    Par défaut

    Récursif est-il le bon terme ?
    Ne cherches-tu pas plutôt à faire ceci :
    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    class Sinus
    {
        int MU;
        public:
        Sinus(): MU( 1 )
        {
     
        }
        Sinus( int mu ): MU( mu )
        {        
            if( mu == 0 )
            {
                cout << "Autre nombre: " << endl;
                cin >> MU;
            }
        }
    };

  8. #8
    Invité de passage
    Homme Profil pro
    Étudiant
    Inscrit en
    décembre 2012
    Messages
    3
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : décembre 2012
    Messages : 3
    Points : 0
    Points
    0

    Par défaut

    Neckara, je n'ai plus ces exemples en tête, je redemanderai à mon prof demain si je le vois.

    leternel, merci pour toutes ces informations sur les constructeurs, je lirai plus attentivement la FAQ sur le sujet. Ce que j'en conclus d'important, c'est qu'ils vaut mieux faire les opérations délicates hors du constructeur.

    MicBeast, oui la question portait sur le récursif. Je n'aurai eu aucun mal à faire un programme qui fonctionne, j'étais simplement curieux de savoir en quoi il ne fonctionnait pas.

  9. #9
    Expert Confirmé Sénior
    Inscrit en
    août 2006
    Messages
    3 561
    Détails du profil
    Informations forums :
    Inscription : août 2006
    Messages : 3 561
    Points : 4 569
    Points
    4 569

    Par défaut

    Koa,

    À mon avis, vouloir faire un constructeur récursif montre une profonde méconnaissance de ce qu'est un constructeur, et si c'est un prof qui a suggéré ça, il faut en changer immédiatement (je sais, je sais : tu n'as pas pas le choix ).
    Il court en ce moment une espèce de grippe, mais elle ne court pas très vite, car on peut l'attraper sans courir.

  10. #10
    Modérateur

    Homme Profil pro Cyrille
    Network programmer
    Inscrit en
    juin 2010
    Messages
    2 178
    Détails du profil
    Informations personnelles :
    Nom : Homme Cyrille
    Âge : 27
    Localisation : France

    Informations professionnelles :
    Activité : Network programmer

    Informations forums :
    Inscription : juin 2010
    Messages : 2 178
    Points : 5 644
    Points
    5 644

    Par défaut

    Bonjour,

    le seul moment où tu fais un appel explicite au constructeur c'est dans le cas d'un héritage où la classe mère a un constructeur avec paramètres.
    Sinon, tu n'as pas à te soucier d'appeler le constructeur, c'est automatique à la création d'un objet.

Liens sociaux

Règles de messages

  • Vous ne pouvez pas créer de nouvelles discussions
  • Vous ne pouvez pas envoyer des réponses
  • Vous ne pouvez pas envoyer des pièces jointes
  • Vous ne pouvez pas modifier vos messages
  •