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 :

Passage de parametres (objets)


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Étudiant
    Inscrit en
    Novembre 2008
    Messages
    35
    Détails du profil
    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2008
    Messages : 35
    Par défaut Passage de parametres (objets)
    Bonjour tout le monde,
    je me lance enfin au c++, et j'ai quelques questions qui me trottent la tête:
    1. Lors du passage d'objets en paramètres d'une fonction (un string par exemple) quel est la syntaxe correcte, sachant que je n'ai pas l'intention de modifier cet objet dans la fonction:
      1 -->
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      void maFonction(const string &s);
      2 -->
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
      void maFonction(const string s);
    2. Si j'ai une fonction qui retourne un objet (toujours un string), comment dois-je procéder:
      1 -->
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
       string maFonction() { string s = "foo"; return s; }
      2 -->
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
       const string maFonction() { string s = "foo"; return s; }
      3 -->
      Code : Sélectionner tout - Visualiser dans une fenêtre à part
       const &string maFonction() { string s = "foo"; return s; }
      4 --> autres ..?
      dans ces cas ou est libéré le string (si il l'est ?)?


    Merci de me répondre

  2. #2
    Membre Expert

    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
    Par défaut
    Ca depends des différentes situtations :

    1-
    1. C'est un passage par référence constante, s sera un alias (ie un autre nom) pour un objet qui existe avant l'appel, et tu ne pourras pas la modifier dans la fonction. Si ta fonction n'a pas besoin de modifier le paramètre et que celui-ci a une taille conséquente, c'est probablement le mieux à faire.

    2. C'est un passage par copie, l'identifiant s se réfère à une variable local à la fonction (ie n'existe pus hors de la fonction) qui est initialisé avec l'objet que tu donnes en paramètre (ca dépends du type, mais souvent grace au constructeur de copie). Dans ce cas le caractère constant ne se justifie pas trop, ta fonction va travailler sur une copie de l'objet d'origine, elle devrait pouvoir en faire ce qu'elle veut sans effet de bord. Ce mode de passage peut avoir son utilité.

    2- Dans les deux premiers cas tu retournes des copies (l'objet s est copié puis détruit à la fin de la fonction), cependant selon la situation un mécanisme d'optimisation (copy-elision) peut intervenir et construire directement l'objet au bon endroit, il n'y a donc plus de copie. La troisième forme n'est pas valide, tu retournes une référence sur quelque chose qui n'existe plus, car s est détruit à la fin de la fonction.

  3. #3
    Membre averti
    Étudiant
    Inscrit en
    Novembre 2008
    Messages
    35
    Détails du profil
    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2008
    Messages : 35
    Par défaut
    Merci beaucoup pour la réponse Flob qui plus est très claire,
    j'ai juste une dernière question:
    sur le dernier point que tu as cité:comme l'objet est détruit en fin de fonction, la forme correcte serait donc:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     const &string maFonction() { string *s = new String("foo"); return s; }
    ?
    Dans ce cas, la libération doit-elle se faire au niveau du code appelant avec un delete?
    Sinon, quel prototype utilises-tu le plus souvent?

    J'espère ne pas trop abuser de ton temps ..

  4. #4
    Membre Expert

    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
    Par défaut
    Je pense que tu as compris où était le problème, mais tu as fait une erreur de syntaxe const &string ne signifie rien (dans ce contexte).

    Et en effet ca sera à l'appelant de détruire la ressource, cependant on essaie de limiter ce genre de chose (laisser la responsabilité de détruire la ressource c'est une invitation aux fuites mémoires et autres problèmes), un moyen de limiter et l'utilisation de capsule RAII, je te laisse faire des recherches sur ce terme pour plus d'information (en commencant par la FaQ).

  5. #5
    Membre Expert
    Avatar de Goten
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 580
    Détails du profil
    Informations personnelles :
    Âge : 34
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 580
    Par défaut
    Citation Envoyé par anubis_1001 Voir le message
    Merci beaucoup pour la réponse Flob qui plus est très claire,
    j'ai juste une dernière question:
    sur le dernier point que tu as cité:comme l'objet est détruit en fin de fonction, la forme correcte serait donc:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     const &string maFonction() { string *s = new String("foo"); return s; }
    ?
    Dans ce cas, la libération doit-elle se faire au niveau du code appelant avec un delete?
    Sinon, quel prototype utilises-tu le plus souvent?

    J'espère ne pas trop abuser de ton temps ..

    void maFonction(std::string& s) { s = "foo"; } marche aussi. (attention je dis pas que c'est ce que j'utilise le plus ...)

  6. #6
    Membre averti
    Étudiant
    Inscrit en
    Novembre 2008
    Messages
    35
    Détails du profil
    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2008
    Messages : 35
    Par défaut
    Merci a vous deux

  7. #7
    Membre averti
    Étudiant
    Inscrit en
    Novembre 2008
    Messages
    35
    Détails du profil
    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2008
    Messages : 35
    Par défaut
    Bonjour,
    je reviens sur le sujet avec une question plus précise..
    Supposons que j'ai la classe suivante:
    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
    class A {
      private:
        B *b;
      public:
        A();
        ~A();
        const B & getReferenceSurB() const; //est-ce le bon prototype pour récupérer la référence sur B?
    }
    A::A() { b = new B(); }
    A::~A() { delete b; }
    const B & getReferenceSurB() const { return *b; }
    
    //.. dans main ensuite ..
    
    int main() {
      A a();
      B b = a.getReferenceSurB(); // dois-je utiliser B &b a la place, et puis-je utiliser b par la suite?
    }
    Je pense que vous avez compris ce que je veux faire, j'aimerais savoir si c'est le meilleur moyen de récupérer la référence sur B.

    Merci d'avance pour votre aide

  8. #8
    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,
    Citation Envoyé par anubis_1001 Voir le message
    Bonjour,
    je reviens sur le sujet avec une question plus précise..
    Supposons que j'ai la classe suivante:
    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
    class A {
      private:
        B *b;
      public:
        A();
        ~A();
        const B & getReferenceSurB() const; //est-ce le bon prototype pour récupérer la référence sur B?
    }
    A::A() { b = new B(); }
    A::~A() { delete b; }
    const B & getReferenceSurB() const { return *b; }
    
    //.. dans main ensuite ..
    
    int main() {
      A a();
      B b = a.getReferenceSurB(); // dois-je utiliser B &b a la place, et puis-je utiliser b par la suite?
    }
    Je pense que vous avez compris ce que je veux faire, j'aimerais savoir si c'est le meilleur moyen de récupérer la référence sur B.

    Merci d'avance pour votre aide
    Le plus facile est peut être d'essayer, afin de voir ce que te dis le compilateur

    Plus sérieusement, il est tout à fait possible de déclarer b comme une variable "normale" ET de déclarer b comme étant une référence (obligatoirement constante) sur un objet de type B. La décision entre l'une ou l'autre solution dépendra essentiellement de ce que tu veux faire de b par la suite, et de quelques conditions qui devront être respectées (ce qui impliquera d'ailleurs certaines conséquences ) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    B b = a.getReferenceSurB();
    /* que l'on pourrait aussi écrire sous la forme de
    B b(a.getReferenceSurB());
    créera b comme étant une copie du membre A::b (en fait comme une copie de ce qui se trouve à l'adresse pointée par A::b, vu que A::b est un pointeur sur B dans ton exemple )

    Tu pourras donc manipuler b strictement à ta guise, en appelant, pourquoi pas, des fonctions non constantes ayant pour but de modifier b.

    Cependant, comme tu manipule seulement une copie de ce qui est pointé par A::b, aucune des modifications que tu pourra apporter ne seront répercutées dans A::b.

    De plus, cela implique que ta classe B soit copy-constructible et / ou assignable.

    De plus, tu ne pourra pas utiliser b de manière polymorphe.

    Le code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    B const & b = a.getReferenceSurB();
    est tout aussi valable, évite la copie de B (ce qui peut s'avérer intéressant) mais interdit toute modification de b, car il s'agit d'une référence constante (tu ne pourra donc invoquer que les fonctions constantes de B)

    Par contre, si la classe B est une classe parent dans une hiérarchie de classe, tu pourras envisager d'invoquer les comportements (qui s'engagent à ne pas modifier l'objet, toujours ) polymorphes qu'elle propose.

    Cela m'amène naturellement à parler de deux considérations connexes:

    Il n'y a que rarement de raison de recourir à l'allocation dynamique pour les membres de classes, exception faite du cas où tu souhaites profiter du polymorphisme apporté par une hiérarchie de classe:

    Un code proche 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
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    class B
    {
       /* what ever */
    };
    class C : public B
    {
       /* what ever */
    };
    class D : public B
    {
       /* what ever */
    };
    class E : public B
    {
       /* what ever */
    };
    class A
    {
        public:
         A(int i)
         {
             switch(i)
             {
                 case 1:
                     b = new C;
                     break;
                 case 2 :
                     b = new D;
                     break;
                 case 3 :
                     b = new E;
                     break;
             }
         }
        private:
            B * b;
    };
    pourrait se justifier dans certains cas, mais uniquement si, comme le code l'indique, B est une classe parent dans une hiérarchie de classe (il n'y a pas vraiment de raison d'allouer dynamiquement b si ce n'est pas le cas car le membre aura, de toutes manières, exactement la durée de vie de l'instance de A dans laquelle il se trouve )

    La deuxième considération connexe a trait à l'accesseur que tu fournis...

    Il existe, en effet, une règle de conception qui s'appelle la "loi demeter".

    Cette loi nous explique que, si la class A utilise en interne une instance de la classe B, l'utilisateur de la classe A (ex: la fonction main ou toute fonction membre d'une classe C qui manipulerait une instance de A) ne devrait pas avoir à connaitre la classe B afin de pouvoir utiliser A:

    Soit A utilise b exclusivement en interne, et c'est alors à A de fournir les comportement qui manipulent b, sans que ce qui manipule A ne sache que b est utilisé, soit b est une instance particulière de la classe, manipulée par A mais gérée par ailleurs.

    Dans le meilleur des cas A ne devrait fournir que l'information permettant d'identifier b de manière unique de manière à ce que la fonction qui passe par A pour retrouver b puisse demander à la classe qui a en charge la gestion des B (la "collection de B's") l'instance correspondante afin de la manipuler

    De cette manière, tu arriverais à clairement séparer les responsabilités et tu pourrais donc éviter d'avoir un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    a.getB().getC().getD().doSomething();
    qui t'oblige à connaitre B, C et D en plus de A alors que... tu travailles avec A (et qui augmente les dépendances de manière catastrophiques )
    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

  9. #9
    Membre averti
    Étudiant
    Inscrit en
    Novembre 2008
    Messages
    35
    Détails du profil
    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2008
    Messages : 35
    Par défaut
    Bonsoir koala,

    Je voulais retourner une référence pour éviter justement que l'objet soit inutilement copié en mémoire (et que le constructeur de copie soit appelé).

    Pour l'initialisation de b, tu m'as dis que je devais éviter de créer un pointeur sur B, mais si je déclarais: B vas être initialisé en appelant le constructeur par default B::B(), or dans mon cas A fourni plusieurs constructeurs, et suivant ces constructeurs B est crée différemment.

    Je réfléchis actuellement sur la possibilité que tu as citée que A hérite de B. Je pense que en effet, dans mon cas, cela peut être plus juste de faire ainsi.

    Merci pour ton aide

  10. #10
    screetch
    Invité(e)
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    private: B b;
    A() : b("Toto") { }
    consulte la doc sur lesl istes d'initialisation et évite le code dans le constructeur/destructeur si tu peux

  11. #11
    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 anubis_1001 Voir le message
    Bonsoir koala,

    Je voulais retourner une référence pour éviter justement que l'objet soit inutilement copié en mémoire (et que le constructeur de copie soit appelé).

    Pour l'initialisation de b, tu m'as dis que je devais éviter de créer un pointeur sur B, mais si je déclarais: B vas être initialisé en appelant le constructeur par default B::B(), or dans mon cas A fourni plusieurs constructeurs, et suivant ces constructeurs B est crée différemment.
    Comme l'a indiqué screetch, rien ne t'empêche d'invoquer explicitement le constructeur qui va bien pour le membre de ta classe en utilisant les listes d'initialisations
    Je réfléchis actuellement sur la possibilité que tu as citée que A hérite de B. Je pense que en effet, dans mon cas, cela peut être plus juste de faire ainsi.

    Merci pour ton aide
    Je n'ai jamais évoqué la possibilité que A hérite de B... j'ai évoqué la possibilité que B soit la classe de base d'une hiérarchie dont A ne fait pas partie, et que tu puisse créer un objet "passant pour" un objet de type B (mais étant d'un type dérivé).

    L'héritage étant la relation la plus forte qui puisse exister entre deux classes, il faut impérativement veiller à ce qu'il reste sensé et sémantiquement correct (en plus de respecter LSP )
    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. DOS passage de parametre à un .bat
    Par malbaladejo dans le forum Scripts/Batch
    Réponses: 5
    Dernier message: 26/11/2014, 15h44
  2. [langage] Passage de parametre lors de l execution
    Par WetzlarMan dans le forum Langage
    Réponses: 4
    Dernier message: 16/03/2004, 13h28
  3. Probleme de passage de parametre a un TQuery
    Par gve21 dans le forum C++Builder
    Réponses: 7
    Dernier message: 15/01/2004, 15h49
  4. Passage de parametre calculé
    Par soazig dans le forum MS SQL Server
    Réponses: 12
    Dernier message: 06/06/2003, 16h25
  5. Passage de parametre a une anim Flash 5
    Par debug dans le forum Intégration
    Réponses: 4
    Dernier message: 03/06/2002, 17h59

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