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 :

Classe membre sans constructeur par defaut


Sujet :

C++

  1. #1
    Membre actif
    Avatar de TheDrev
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    310
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Novembre 2006
    Messages : 310
    Points : 263
    Points
    263
    Par défaut Classe membre sans constructeur par defaut
    Bonjour. Sachant que lors qu'un constructeur est declare, aucun constructeur par defaut n'est fournis. comment initialiser une propriété d'une instance membre d'une classe ?

    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
     
    class A{
      B membre;
     
     
    };
     
    class B{
    public :   
    B(int valeur){  //pas de constructeur par defaut
      i = valeur;  
    }
     
    private : 
    int i;
    };
    lors de la declaration de 'membre' dans A, le compilateur me signale qu'il n existe pas de B::B(), mais je veut initialiser i !

  2. #2
    Membre à l'essai
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    9
    Détails du profil
    Informations personnelles :
    Âge : 34
    Localisation : France

    Informations forums :
    Inscription : Septembre 2007
    Messages : 9
    Points : 10
    Points
    10
    Par défaut
    Bonsoir,

    Tu es sûr de ce que tu as écris ? on dirait que tu as mélanger pas mal de choses.

    Ne serait-ce pas plutôt :
    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
    class B {
     
        public:
        B(int valeur) { i = valeur; }
     
        private:
        int i;
     
    };
     
    class A {
     
        public:
     
        private:
        B membre;
     
    };

  3. #3
    Membre averti
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Paris (Île de France)

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

    Informations forums :
    Inscription : Mai 2006
    Messages : 366
    Points : 444
    Points
    444
    Par défaut
    Il faut que tu fournisses le paramètre pour le constructeur de B dans le constructeur de A, et que tu appelles le constructeur de B depuis celui-ci. Par ailleurs il vaut mieux utiliser les listes d'initialisation :

    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 B {
     
        public:
     
            B(int i) : valeur(i) {}
     
        private:
     
            int valeur;
     
    };
     
    classe A {
     
        public:
     
            A(int i) : membre(i) {}
     
        private:
     
            B membre;
     
    };

  4. #4
    Membre actif
    Avatar de TheDrev
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    310
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Novembre 2006
    Messages : 310
    Points : 263
    Points
    263
    Par défaut
    Willy :
    Effectivement, j'ai inversé l'odre des definitions dans l'exemple.

    bolhrak :
    C'est ce que je cherchais ! j'aurai d'ailleur du me creuser plus la tete, mais je ne connaissais pas du tout les listes d'initialisations !

    Merci à tout les deux.

  5. #5
    Membre confirmé
    Profil pro
    Inscrit en
    Juin 2005
    Messages
    731
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2005
    Messages : 731
    Points : 574
    Points
    574
    Par défaut
    Je ne comprends pas trop le problème :
    qu'un constructeur existe (et déclaré par toi) ou non dans ta classe n'initialise de toute façon en aucune mesure des variables membres de type littéral (style ton int).
    Donc 2 solutions :
    - soit tu définis les deux constructeurs, l'un prenant un int en paramètre et initialisant la valeur de ton membre avec cet int ; l'autre ne prenant aucun paramètre et initialisant ton int à 0 ou -1 ... enfin la valeur que tu veux.
    - soit tu déclares ton constructeur prenant l'int en paramètre explicit pour éviter que quelqu'un utilise le constructeur par défaut de ta classe.

  6. #6
    screetch
    Invité(e)
    Par défaut
    declarer le constructeur explicite n'empeche pas d'utiliser le constructeur par defaut

    ne pas donner de constructeur par defaut interdit simplement d'initialiser un B sans donner la valeur que l'entier doit prendre. La reponse donnée plus haut est la bonne.

    Par contre il serait quand meme bon de declarer le constructeur de B prenant un parametre explicite, pour eviter les conversions automatiques involontaires.

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

    Informations professionnelles :
    Activité : aucun

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

    Il est important de comprendre le rôle du constructeur, pour comprendre réellement l'erreur, bien que la liste d'initialisation soit la solution

    Le constructeur va travailler en quatre temps:
    1. Allouer la mémoire nécessaire pour contenir l'ensemble des données de la classe
    2. Appeler le constructeur de la classe parent (s'il y en a une)
    3. Appeler le constructeur des différents membres de la classe dans l'ordre de déclaration des membres
    4. Effectuer les instructions éventuelles qu'il contient, à l'instar de n'importe quelle autre fonction.

    Si une classe (B) sert de membre à une autre classe (A) et que l'on ne met pas explicitement l'appel au constructeur de B dans la liste d'initialisation de A, lorsque le constructeur arrive à l'étape [3](appel des constructeurs des différents membres), il appellera le seul constructeur de B qui, selon lui, devrait correspondre: celui qui ne prend pas d'argument (le constructeur par défaut).

    Il est à noter qu'il réagira de la même manière lors de l'étape [2] (appel du constructeur de la classe parent, dans le cas d'un héritage).

    Or, si le seul constructeur que tu déclares (et définis) prend ne serait-ce qu'un argument obligatoire, il est logique que le système ne trouve pas le constructeur par défaut... Raison de l'erreur obtenue

    Maintenant, il faut savoir que l'on peut rendre certains paramètres de fonction facultatifs (pour autant que, s'il y a un paramètre qui suit un paramètre facultatif, le paramètre qui suit soit aussi facultatif) en lui fournissant une valeur par défaut.

    Ainsi, tu pourrais très bien envisager une classe B prenant la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    class B
    {
        public:
            B(int i=0):m(i){}
    /*...*/
        private:
            int m;
    };
    Comme le constructeur de B connaît une valeur pour son paramètre si l'utilisateur ne lui en donne pas, on peut appeler un constructeur sous la forme de B b(); qui aura pour effet de fournir d'office la valeur 0 au membre m.

    Enfin, il faut savoir qu'un code source est lu, comme par n'importe qui, du haut vers le bas (et de gauche à droite), et que le système, au moment de la compilation ne connaît que ce qu'il a déjà rencontré au par avant.

    Ainsi, si tu crées un fichier d'en-tête du genre de
    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
     
    #include "unfichier"
    #ifndef CLASS_A_B_INLUDED_H  //"guard dog" contre l'inclusion multiple
    #define CLASS_A_B_INCLUDED_H
    class A 
    {
        public:
            A(int i):b(i){}
            ~A(){}
            /*...*/
        private:
            B b;
    };
    class B
    {
        public:
            B(int i):m(i){}
            ~B(){}
            /*...*/
        private:
            int m;
    };
    #endif //CLASS_A_B_INCLUDED_H
    Lorsque le système devra gérer la classe A, il aura connaissance:
    • De ce qui se trouve dans "unfichier"
    • De la classe A

    mais ne saura absolument rien... de la classe B :roll (quand tu lis un livre, et que tu es en haut de la page 3, tu n'a aucune idée de ce qui se passe en bas de la page, et encore mois de ce qui se passe à la page 10 )

    Il se plaindra alors du fait que "la classe B n'est pas déclarée".

    Tant que la dépendance ne va que "dans un sens" (la classe A qui a besoin d'une classe B), il "suffira" d'inverser les déclarations de manière à ce que B soit déclarée avant la classe A pour que tout rentre dans l'ordre.

    Par contre, les choses se compliquent si on a des dépendances dans les deux sens (la classe A qui a besoin de la classe B et la classe B qui a besoin de la classe A)... Et, contrairement à ce que tu pourrais croire, c'est loin d'être trivial

    En effet, si on déclare A avant B, lorsque le système gérera B, il connaîtra A, mais, au moment de gérer A, il ne saura rien de B, et inversement.

    Dans ce cas, on aura recours à ce que l'on appelle la "déclaration anticipée".

    L'idée est simple: dire au compilateur que "la classe existe", mais qu'il aura les informations la concernant plus tard

    Cela se fait "simplement" en ajoutant une ligne class Nom_de_classe; en tout état de cause avant la première classe dans laquelle apparaît la dépendance croisée.

    Le code précédent pourrait alors devenir
    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
     
    #include "unfichier"
    #ifndef CLASS_A_B_INLUDED_H  //"guard dog" contre l'inclusion multiple
    #define CLASS_A_B_INCLUDED_H
    class B;
    class A 
    {
        public:
            A(int i):b(i){}
            ~A(){}
            /*...*/
        private:
            B b;//dépendance avec B
    };
    class B
    {
        public:
            B(int i):m(i){}
            ~B(){}
            /*...*/
            void Fonction(const A&);//dépendance avec A
        private:
            int m;
    };
    #endif //CLASS_A_B_INCLUDED_H

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

Discussions similaires

  1. syntaxe, init membre sans constructeur par défaut
    Par Ghurdyl dans le forum Débuter
    Réponses: 3
    Dernier message: 17/07/2009, 16h33
  2. Réponses: 7
    Dernier message: 29/01/2009, 13h32
  3. Réponses: 9
    Dernier message: 03/08/2008, 15h45
  4. Réponses: 14
    Dernier message: 17/11/2006, 20h17
  5. Réponses: 4
    Dernier message: 05/02/2004, 19h18

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