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 :

pointeurs et références quoi choisir ?


Sujet :

C++

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Février 2007
    Messages
    382
    Détails du profil
    Informations personnelles :
    Âge : 35
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 382
    Points : 73
    Points
    73
    Par défaut pointeurs et références quoi choisir ?
    Bonjour,

    J'aimerais avoir votre aide parce que je suis un peu perdu et je ne sais pas comment faire. En effet dans quel cas utiliser un pointeur ou une référence ?

    Exemple dans le code suivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    class Node
    {
        public:
           Node *getParent() const;
           void setParent(const Node *);
       private:
           Node *parent ;
    }
    Nous utilisons les pointeurs. Mais serais-il possible d'utiliser les références ?...

    merci de votre aide

  2. #2
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 275
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 275
    Points : 10 985
    Points
    10 985
    Par défaut
    Non. Une référence est un alias qui ne peut être ré-attaché.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  3. #3
    Membre régulier
    Profil pro
    Inscrit en
    Février 2007
    Messages
    382
    Détails du profil
    Informations personnelles :
    Âge : 35
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 382
    Points : 73
    Points
    73
    Par défaut
    Peux tu donner un peu plus de détail. Ca ne m'aide pas beaucoup.

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

    Informations professionnelles :
    Activité : aucun

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

    En gros, un pointeur doit être utilisé, principalement, dans quatre circonstances:

    • Quand tu veux pouvoir bénéficier du polymorphisme
    • Quand une structure doit disposer de membre de son propre type (ou d'un type dérivé/parent).
    • *éventuellement* quand un membre doit "survivre" à la classe qui le contient ou quand il doit pouvoir ne pas être initialisé... et que tu dois pouvoir tester s'il l'est ou non.
    • Quand un argument passé en fonction peut ne pas exister (ce qui est tout à fait différent de "peut etre vide" )

    En effet, il est impossible qu'un Noeud en contienne "physiquement" un autre... Tout ce qu'il peut contenir, c'est un rappel de l'adresse à laquelle il se trouve

    Dans tous les cas, il s'agira de t'assurer que la libération de la mémoire allouée au pointeur soit correctement libérée au bon moment: ni trop tôt, ni trop tard

    Si tu veux éviter la copie d'un élément, et que tu n'est pas dans l'un de ces quatre cas bien particulier, c'est donc une référence qu'il te faudra utiliser

    Pour être précis: un pointeur est l'adresse à laquelle tu trouvera une variable du type donné, alors que la référence n'est qu'un alias (un autre nom) d'une instance donnée du type en question...
    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

  5. #5
    Rédacteur

    Avatar de ram-0000
    Homme Profil pro
    Consultant en sécurité
    Inscrit en
    Mai 2007
    Messages
    11 517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Consultant en sécurité
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2007
    Messages : 11 517
    Points : 50 367
    Points
    50 367
    Par défaut
    Je rajouterai une autre utilité

    Dans un passage de paramètres à une fonction, une référence doit être utilisée lorsque cela n'a pas de sens que son équivalent pointeur soit NULL.

    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
     
    bool OpenFile(const string *Filename /*= NULL*/)
    {
       if(Filename != NULL)
       {
          // open the file
          ...
       }
       else
       {
          // open stdin
          ...
       }
       ...
    }
    Dans ce cas, le fait que Filename soit NULL a un sens (pour ma fonction) donc je peux (et même je dois) utiliser un pointeur
    Raymond
    Vous souhaitez participer à la rubrique Réseaux ? Contactez-moi

    Cafuro Cafuro est un outil SNMP dont le but est d'aider les administrateurs système et réseau à configurer leurs équipements SNMP réseau.
    e-verbe Un logiciel de conjugaison des verbes de la langue française.

    Ma page personnelle sur DVP
    .

  6. #6
    Membre confirmé

    Inscrit en
    Août 2007
    Messages
    300
    Détails du profil
    Informations forums :
    Inscription : Août 2007
    Messages : 300
    Points : 527
    Points
    527
    Par défaut
    Citation Envoyé par damien77 Voir le message
    Peux tu donner un peu plus de détail. Ca ne m'aide pas beaucoup.
    Le point crucial pour cette classe est bien celui qu'indique Luc hermitte: une référence est une sorte de pointeur constant dépointé. Il est donc impossible de changer vers quoi pointe la référence après sa création, ce qui élimine la possibilité d'implémenter le "parent" en tant que référence ET d'avoir une fonction setParent.
    D'une manière générale, les références membres de classe sont à manipuler avec encore plus de précautions que les pointeurs membres. Certes, les pointeurs membres "nus" nécessitent en général la définition du destructeur, du constructeur de copie, et de l'opérateur d'affectation, ce qui n'est pas aussi simple d'utilisation que les boost::smart_ptr. Par contre, lorsqu'on a un membre référence, la sémantique de l'opérateur d'affectation va être très difficile à respecter (impossible de changer la référence après-coup), et il n'y a pas de constructeur par défaut automatiquement généré. Cela limite fortement les algorithmes génériques pouvant être appliqués à la classe, car ces deux opérations font très souvent partie des pré-requis des paramètres de classes génériques.
    "Maybe C++0x will inspire people to write tutorials emphasizing simple use, rather than just papers showing off cleverness." - Bjarne Stroustrup
    "Modern C++11 is not your daddy’s C++" - Herb Sutter

  7. #7
    screetch
    Invité(e)
    Par défaut
    une reference indique une relation 1:1 avec une autre classe. c'est a dire que tu referencesune et une seule instance d'une autre classe ( tu la partage, tu ne la possedes pas ). De plus, tu ne peux pas changer l'objet que tu references.

    les pointeurs sont souvent utilisés en relation 0:1 ou meme 0:n (comme une reference optionnelle, ou bien comme un tableau de 0 a n references). de plus cette reference vers une autre instance peut etre changée.

    Si tu as une relation "forte" 1:1, alors tu devrais utiliser des references.

    En résumé, reference =
    -> pointeur faible (pas de partage de propriété)
    -> non optionnel (tu dois des le debut pointer vers quelque chose)
    -> impossible de "rebinder" sur autre chose

    utiliser un pointeur (intelligent ou pas) si l'une de ces contraintes est relachée.

    Tu peux verifier que pour les parametres de fonction c'est exactement le cas :
    -> pointeur faile (tu ne possedes pas le parametre, c'est l'appelant qui le possede)
    -> non optionnel (tu dois toujours passer un parametre ou bien le parametre par defaut)
    -> non rebindable : tu ne peux plus, une fois dans la fonction, revenir en arriere pour passer j au lieu de i

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par ram_0000 Voir le message
    Je rajouterai une autre utilité

    Dans un passage de paramètres à une fonction, une référence doit être utilisée lorsque cela n'a pas de sens que son équivalent pointeur soit NULL.

    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
     
    bool OpenFile(const string *Filename /*= NULL*/)
    {
       if(Filename != NULL)
       {
          // open the file
          ...
       }
       else
       {
          // open stdin
          ...
       }
       ...
    }
    Dans ce cas, le fait que Filename soit NULL a un sens (pour ma fonction) donc je peux (et même je dois) utiliser un pointeur
    Non, car tu pourrais très bien vérifier si la chaine est vide (ou non), et, en tout cas, la passer en tant que référence, idéalement constante

    Je suis d'accord avec le fait qu'un argument qui peut ne pas exister sera fourni sous la forme d'un pointeur, vallant NULL s'il n'existe effectivement pas, ce qui se rapproche du troisième cas cité (allez, je vais rajouter le cas des arguments )

    Mais on déconseille fortement de fournir un pointeur sur tout ce qui vient de la S(T)L:

    std::string, conteneurs flux et, d'ailleurs l'ensemble de la S(T)L sont à fournir "par principe" et de préférence en tant que référence, constante s'il échoit
    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
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Si le code fait une différence entre une string vide et pas de string du tout, alors là c'est bien un pointeur qu'il faudra utiliser, par contre...
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Si le code fait une différence entre une string vide et pas de string du tout, alors là c'est bien un pointeur qu'il faudra utiliser, par contre...
    Oui, mais tel que présenté ici, le code ne permet pas de déterminer s'il fait effectivement une telle distinction...

    En plus, le code pourrait très bien s'appliquer, en l'occurrence, avec une chaine vide
    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 régulier
    Profil pro
    Inscrit en
    Février 2007
    Messages
    382
    Détails du profil
    Informations personnelles :
    Âge : 35
    Localisation : France

    Informations forums :
    Inscription : Février 2007
    Messages : 382
    Points : 73
    Points
    73
    Par défaut
    D'accord donc pour résumer dans ce cas là.

    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 RNode
    {
        public:
            void setParent(const Node &);
        private:
            Node parent ;
    }
     
    class PNode
    {
        public:
            void setParent(const Node *);
        private:
            Node *parent ;
    }
    La référence permet de manipuler l'objet original au moment de l'appel de la fonction mais par la suite nous avons une copie...

    La node permet de manipuler et stocker l'adresse mémoire de notre objet.

  12. #12
    Membre éclairé Avatar de MatRem
    Profil pro
    Inscrit en
    Décembre 2002
    Messages
    750
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2002
    Messages : 750
    Points : 693
    Points
    693
    Par défaut
    Tu peux aussi très bien stocker un pointeur vers l'objet en passant par une référence dans la fonction.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class Node
    {
    public:
       void setParent(Node const & parent);
    private:
       Node const * _parent ;
    }

  13. #13
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Mais je trouve ça crade au possible.

    Et ça nuit à la lisibilité du code. Un code pareil, je le rejette.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  14. #14
    Membre éprouvé
    Profil pro
    Inscrit en
    Mai 2006
    Messages
    780
    Détails du profil
    Informations personnelles :
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Mai 2006
    Messages : 780
    Points : 1 176
    Points
    1 176
    Par défaut
    comme un Node peut ne pas avoir de parents, tu n'as pas trop le choix, tu ne peux pas utiliser de références.

  15. #15
    Expert confirmé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Points : 4 551
    Points
    4 551
    Par défaut
    Citation Envoyé par MatRem Voir le message
    Tu peux aussi très bien stocker un pointeur vers l'objet en passant par une référence dans la fonction.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    class Node
    {
    public:
       void setParent(Node const & parent);
    private:
       Node const * _parent ;
    }
    C'est excessivement risqué, puisque ça permet de passer à la fonction setParent() un instance locale à une fonction de manière implicite - c'est à dire sans que le programmeur ait à reflechir à ce qu'il fait. Si il a l'obligation de rajouter l'opérateur &, il voit de suite qu'il passe en paramètre l'adresse d'un objet automatique, et il se pose la question de la légalité de ce qu'il écrit.

    Un semblant de règle :
    1. si l'objet doit avoir une durée de vie qui n'est pas automatique : utiliser un pointeur
    2. si la référence sur un objet peut être invalide : utiliser un pointeur (pour comparaison avec NULL)
    3. si la référence sur un objet peut varier pendant l'exécution du programme : utiliser un pointeur.
    4. dans le cas contraire : une référence peut probablement faire l'affaire.
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  16. #16
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Points : 4 625
    Points
    4 625
    Par défaut
    si la référence sur un objet peut être invalide : utiliser un pointeur (pour comparaison avec NULL)
    Ou pas. Utiliser un bit particulier ou rajouter un booléen marche aussi, et est plus performant car tu n'es pas obligé d'allouer ton objet dynamiquement comme cela est nécessaire dans certains cas.
    Boost ftw

  17. #17
    screetch
    Invité(e)
    Par défaut
    ca ne change rien a l'allocation, une reference ne te permettra pas de changer une alloc dynamique en alloc statique par magie.

  18. #18
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Points : 4 625
    Points
    4 625
    Par défaut
    Tu fais un POD de la taille et de l'alignement de ton type, et tu rajoutes un booléen pour indiquer si il est initialisé ou pas.
    Quand tu veux construire ton élément, tu fais un placement new sur l'adresse de ton POD et tu mets le booléen à vrai.
    Dans le destructeur, si le booléen vaut vrai tu appelles le destructeur de ton type.

    C'est comme ça qu'est implémenté boost.optional.
    Boost ftw

  19. #19
    screetch
    Invité(e)
    Par défaut
    une reference n'est pas du tout pareille. la, tu es l'unique proprietaire de l'objet. une reference, tu n'es meme pas l'un des proprietaires.

    dans l'exemple precedent, il faudra m'expliquer comment tu peux stocker ton parent a l'interieur de toi meme dans un pod...

  20. #20
    doccpu
    Invité(e)
    Par défaut
    Citation Envoyé par loufoque Voir le message
    Ou pas. Utiliser un bit particulier ou rajouter un booléen marche aussi, et est plus performant car tu n'es pas obligé d'allouer ton objet dynamiquement comme cela est nécessaire dans certains cas.
    Erreur : une référence sur un NULL est impossible donc si l'on passe un pointeur NULL à une méthode qui attend une référence ça bug a tous les coups !

Discussions similaires

  1. comment choisir pointeurs et références
    Par ikuzar dans le forum Débuter
    Réponses: 5
    Dernier message: 05/07/2012, 07h14
  2. pointeur ou référence
    Par damien77 dans le forum C++
    Réponses: 2
    Dernier message: 23/03/2007, 16h43
  3. Formation admin réseaux / quoi choisir ?
    Par linna dans le forum Emploi
    Réponses: 1
    Dernier message: 03/01/2007, 23h17
  4. Réponses: 5
    Dernier message: 28/11/2006, 13h13
  5. [Applet - Servlet] Communication : quoi choisir ?
    Par gandalf_le_blanc dans le forum Applets
    Réponses: 14
    Dernier message: 28/04/2004, 15h43

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