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 :

Déclaration de fonction un peu bizzare


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre très actif
    Avatar de ProgVal
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2006
    Messages
    636
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 31
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2006
    Messages : 636
    Par défaut Déclaration de fonction un peu bizzare
    Bonjour,
    Dans le manuel Thinking in C++ (merci pour la traduction), j'ai trouvé approximativement ce code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    //définition de la classe
      int i;
    private
      void f(int ii) : i(ii) { }; //Je ne suis pas sûr qu'il y ait le point-virgule.
    }
    Quelqu'un peut-il m'expliquer ce que cela signifie?
    Dans cette question, je comprends:
    -Que signifit i(ii) ?
    -Pourquoi ce code n'est-il pas dans la portée?

    Merci d'avance,
    ProgVal

  2. #2
    Membre émérite
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    1 064
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2005
    Messages : 1 064
    Par défaut
    Ce serait pas plutot
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    NomDeLaClasse(int ii) : i(ii) {}
    ?
    Dans ce cas ça veut simplement dire que le constructeur de ta classe appelle le constructeur de copie de la variable i (qui n'en a pas vraiment puisqu'il s'agit d'un int, mais en C++ on fait "comme si").
    Et non, on a jamais besoin d'un point virgule après l'accolade fermante d'une fonction.

  3. #3
    Membre chevronné
    Avatar de ZouBi
    Inscrit en
    Octobre 2007
    Messages
    508
    Détails du profil
    Informations professionnelles :
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Octobre 2007
    Messages : 508
    Par défaut
    Le code serait exactement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    class f
    {
        private:
        int i;
     
        public:
        f(int ii) : i(ii) { }
    }; // point-virgule ici
    Plusieurs erreurs :
    - En effet, il n'y a pas de ; après les accolades.
    - Par contre, il y a un point virgule après la définition de la classe, juste après l'accolade.
    - Le constructeur ne renvoit aucun type, ni meme void.
    - Le constructeur est public ( ici, il était private, mais peut être que ça marche aussi? ).

    Ce code n'est pas dans la portée, car il est très court, donc pas besoin puisqu'il nuit pas à la lisibilité du code.

  4. #4
    Membre émérite
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    1 064
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2005
    Messages : 1 064
    Par défaut
    Citation Envoyé par ZouBi Voir le message
    - Le constructeur est public ( ici, il était private, mais peut être que ça marche aussi? ).
    Il y a des cas où ça se justifie, notamment si on veut rendre une classe non copiable. Mais j'ai aussi rencontré des cas plus bizarres où ça restait correct (avec des fonction/classes friend par exemple, ou simplement pour pouvoir construire une nouvelle instance à l'intérieur de la classe en lui passant des paramètres pré calculés) -> bref c'est suivant les besoins.

  5. #5
    Membre éclairé
    Homme Profil pro
    Freelance
    Inscrit en
    Décembre 2003
    Messages
    423
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France

    Informations professionnelles :
    Activité : Freelance

    Informations forums :
    Inscription : Décembre 2003
    Messages : 423
    Par défaut
    Citation Envoyé par ProgVal Voir le message
    Dans cette question, je comprends:
    -Que signifit i(ii) ?
    C'est tout simplement une initialisation à la volée, c'est équivalent au code suivant mais en plus performant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
     f(int ii)
    {
      i=ii;
    }

  6. #6
    Membre émérite
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    1 064
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : Belgique

    Informations forums :
    Inscription : Mars 2005
    Messages : 1 064
    Par défaut
    Plus précisément, dans le cas d'un int et en mode release (avec tous les paramètres de compilation classique) on peut être presque à 100% sur que ça ne change rien.
    Par contre dans le cas des classes ça peut jouer beaucoup. Dans le cas d'un string par exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    f(const std::string& ss) {
       s=ss;
    }
    Beaucoup d'implémentations allouent une chaine vide dans le constructeur par défaut (pour rappel une chaine vide n'est pas... vide, c'est 1 char qui contient 0). Si tu n'utilise pas l'initialisation à la volée, le constructeur par défaut de std::string est appelé, suivi de son opérateur d'affectation. Ce qui fait deux allocations de mémoire alors qu'une seule est nécessaire si tu appelles son constructeur de copie:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    f(const std::string& ss):s(ss) {}
    Cela est sans compter que certaines classes ne disposent tout simplement pas de constructeur par défaut.
    Bref l'initialisation à la volée est une bonne habitude à prendre.

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

    On va peut être reprendre depuis de début:

    La fonction présentée est le constructeur de la classe.

    Un constructeur, c'est une fonction qui est automatiquement appelée chaque fois que tu veux créer une nouvelle instance de la classe, que ce soit dans la pile sous la forme de
    ou sur le tas sous la forme de
    Le rôle de cette fonction "particulière" (en ce qu'elle ne fournit pas de valeur de retour) est simplement de s'assurer qu'elle fournira un objet "prêt à l'usage", c'est à dire que tous les membres que la classe contient auront, au minimum, été construits, voire, idéalement, initialisés.

    Pour se faire, lorsque le constructeur est appelé, il va faire appel au constructeur de la classe mère s'il échoit (dans le cadre d'un héritage), puis à celui de chacun des membres de la classe, dans l'ordre de leur déclaration, avant même d'entrer dans le bloc d'instructions de la fonction.

    Mais les problèmes vont commencer à ce point:

    Tu l'auras remarqué, une classe peut avoir une autre classe comme membre, mais le constructeur de cette autre classe peut nécessiter d'avoir certaines valeurs pour n'initialiser correctement.

    Or, si tu ne dis rien au compilateur, il va essayer d'appeler le constructeur du membre qui ne prend aucune valeur en argument de cette autre classe. Et ce constructeur *peut* exister... ou non.

    S'il existe, un constructeur ne prenant pas d'argument obligatoire pour le membre, et que tu écrit le constructeur de ta classe (nommons la f pour respecter ton exemple)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    f::f (ClassMembre & mm)
    {
        m=mm;
    }
    la construction de l'instance de f se fera en deux temps:
    1. construction d'une instance de type ClassMembre pour m
    2. copie de l'instance mm dans le membre m

    ce qui, d'un point de vue des performances peut s'avérer exécrable car la classe ClassMembre peut elle même contenir énormément d'informations qui devront elles-aussi être créées avant d'être recopiées. Et on peut repartir dans la même logique pour tous les membres de la classe ClassMembre.

    Par contre, s'il n'existe aucun constructeur ne prenant aucun argument obligatoire pour le type d'objet du membre, le compilateur se plaindra de ne pas trouver le moyen de créer le membre.

    La solution à ces deux problèmes, c'est, tout simplement, de s'assurer que les différents membres seront directement construits avec les valeurs qui nous intéressent.

    Pour ce faire, le constructeur dispose de ce que l'on appelle un liste d'initialisation.

    C'est ce que l'on trouve entre la parenthèse fermante de la signature du constructeur et l'accolade ouvrante de la portée représentant le groupe d'instructions que le constructeur doit effectuer.

    L'avantage direct, c'est que tu deviens beaucoup plus libre dans le choix de la manière d'implémenter tes constructeurs:

    Ainsi, le constructeur de ta classe f peut prendre une référence sur une instance du type ClassMembre, et ressembler alors à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    f::f(const ClassMembre & mm):m(mm){}
    /*                           ____
                                   ^
                                   |
        Liste d'initialisation qui appelle le constructeur par copie
    */
    mais, s'il existe un constructeur pour ClassMembre nécessitant un entier pour fonctionner, tu pourrais aussi prévoir un constructeur pour f qui prend un entier et se "contente" d'appeler le constructeur de ClassMembre en question
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    f::f(int i): m(i){}
    /*           ___
                  ^
                  |
        Liste d'initialisation qui appelle le constructeur nécessitant un entier
    */
    Evidemment, ce qui est vrai si tu n'a qu'un seul membre est vrai quel que soit le nombre de membres et de valeurs à fournir envisagées... Il suffit de séparer les différents éléments de la liste d'initialisation par une virgule
    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

Discussions similaires

  1. Déclaration de fonction dans le "main".
    Par Pragmateek dans le forum C++
    Réponses: 14
    Dernier message: 23/06/2006, 19h32
  2. déclaration de fonctions externes
    Par poukill dans le forum C++
    Réponses: 40
    Dernier message: 17/05/2006, 16h15
  3. Réponses: 4
    Dernier message: 11/05/2006, 15h34
  4. Réponses: 6
    Dernier message: 20/11/2005, 02h53
  5. [JS] Problème déclaration de fonction
    Par glloq8 dans le forum Général JavaScript
    Réponses: 5
    Dernier message: 23/09/2005, 10h22

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