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 VS Références - Plus rien ne va


Sujet :

C++

  1. #1
    Futur Membre du Club
    Femme Profil pro
    Étudiant
    Inscrit en
    Août 2011
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Août 2011
    Messages : 7
    Points : 5
    Points
    5
    Par défaut Pointeurs VS Références - Plus rien ne va
    Bonjour,

    Le principe des pointeurs et des références est assez nouveau pour moi et, après plusieurs heures de recherche, je n'arrive toujours pas à comprendre comment arranger mon code.

    J'ai la méthode suivante:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void LeafNode::callConstructor(Node& parentNode, int kVar[], int kLeaf[], int &kLeafCount, int &leafCount){
    [...]
    LeafNode * tmp = new LeafNode( parentNode, this ,kVar , kLeaf, kLeafCount, leafCount ); /*A*/
    [...]
    }
    Le constructeur de la ligne A est déclaré comme suit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    LeafNode::LeafNode (Node * parentNode, LeafNode * original ,int kVar[], int kLeaf[], int &kLeafCount, int &leafCount)
    La méthode callConstructor modifie à un certain moment l'objet parentNode, c'est pourquoi je le met en référence (&). Mais, à partir de là, tout s'embrouille entre référence, pointeur et objet.

    Est-ce qu'il faut vraiment utiliser une référence dans cette situation? Si non, comment je peux arriver à modifier l'objet? Si oui, comment je peux convertir cette référence en pointeur pour l'envoyer vers mon constructeur de LeafNode?

    Merci d'avance, et pardonnez mon ignorance, je débute!

  2. #2
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Tout d'abord, ajouter les balises de code simplifierait grandement la lecture.

    Ensuite, il y a une règle simple dans les arguments de fonctions/méthodes : tu mets des références partout où tu mettrais des pointeurs (à moins que tu préfères les pointeurs).

    La différence est que les références te permettent, dans le corps de la fonction, d'utiliser tes arguments avec une syntaxe sans pointeur, comme si tu avais passé les paramètres par copie.
    Tu peux aller consulter cette page, si ça t'éclaircit les idées.

  3. #3
    Futur Membre du Club
    Femme Profil pro
    Étudiant
    Inscrit en
    Août 2011
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Août 2011
    Messages : 7
    Points : 5
    Points
    5
    Par défaut
    Merci beaucoup pour cette réponse rapide! Je comprends mieux de quoi il s'agit maintenant!

    Citation Envoyé par oodini Voir le message

    Ensuite, il y a une règle simple dans les arguments de fonctions/méthodes : tu mets des références partout où tu mettrais des pointeurs
    J'ai fait cette modification. Par contre, mon problème est que je dois, à un moment dans la méthode callConstructor prendre mon parentNode et l'ajouter à un vecteur de pointeurs. Comment est-ce que je peux faire la modification de &Node à *Node?

  4. #4
    Membre éclairé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Points : 764
    Points
    764
    Par défaut
    Tu peux convertir ta référence en un pointeur de la manière suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void LeafNode::callConstructor(Node& parentNode, ...)
    {
        LeafNode * tmp = new LeafNode(&parentNode, ...);
    }
    L'opérateur '&' permet de prendre l'adresse d'un objet (ou d'une référence à un objet en l’occurrence), et donc de former un pointeur.
    Mais si LeafNode attend un pointeur, il est peut être plus judicieux que callConstructor en fasse autant pour que le code soit plus cohérent. Ça ne change pratiquement rien au final, mais avoir une syntaxe constante aide a programmer plus rapidement.

    Sinon, pour choisir entre référence et pointeur, les différences les plus importantes à mon avis sont que :
    • il est plus difficile d'avoir une référence invalide qu'un pointeur invalide (mais ce n'est pas impossible !)
    • une référence est toujours initialisée et on ne peut pas la faire référencer un autre objet a fortiori

    (comme le dit oodini, tu as en plus un changement de syntaxe mais là ce n'est qu'une question de goût (ou de philosophie))

    Du coup selon la fonction, tu auras plus intérêt à prendre une référence ou un pointeur. Un exemple où le pointeur est pratique : tu as une classe Explorateur qui permet de visualiser des Fichiers. Pour sélectionner un fichier, tu peux avoir une méthode du genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void Explorateur::SélectionnerFichier(Fichier* pFichier)
    {
        if (pFichier)
            // Sélection d'un fichier
        else
            // Désélection : aucun fichier sélectionné
    }
    On peut donc appeler cette méthode avec un pointeur valide, et on sélectionne le fichier en question, ou avec un pointeur nul, et on sélectionne "pas de fichier". Ça a un sens.
    Si tu travaillais avec des références, tu aurais dû faire deux fonctions, SélectionnerFichier(Fichier& mFichier) et DésélectionnerFichier().

    A l'inverse, pour une méthode du genre OuvrirFichier(), ça n'aurait pas de sens de l'appeler avec un pointeur nul (on ne peut pas ouvrir "pas de fichier"). Il vaudrait donc mieux utiliser une référence ici.

    Personnellement, je trouve qu'il est "moche" d'avoir des méthodes qui prennent des Fichier* et d'autres des Fichier& (c'est une question de goût hein). Alors la plupart du temps je choisi l'un ou l'autre et je m'y tiens pour toutes les méthodes de la classe.

    Par ailleurs et pour finir, tu peux aussi être grandement influencé par la manière dont sont stockés les Fichiers.
    Mettons que, toujours dans cet exemple, ce soit la classe DisqueDur qui te donne les fichiers en utilisant RécupérerListeFichierDansRépertoireCourrant() (pfiou), elle peut retourner soit un std::vector<Fichier*> soit un std::vector<Fichier> (ou autre...).
    Selon le cas, tu auras plus intérêt à manipuler des Fichier* ou des Fichier& dans ton code, pour éviter d'avoir à utiliser '&' ou '*' à tout bout de champ.

  5. #5
    Futur Membre du Club
    Femme Profil pro
    Étudiant
    Inscrit en
    Août 2011
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Août 2011
    Messages : 7
    Points : 5
    Points
    5
    Par défaut
    Merci Kalith pour la précision.

    J'ai maintenant un autre problème en lien avec les pointeurs et les références.
    J'appelle récursivement la méthode
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    void Node::copyTree( Node & original, int kVar[], int kLeaf[], int &kLeafCount, int &leafCount )
    Lors du premier appel, je passe les paramètres au bon format. Mais lors du 2e appel (dans la fonction), je n'arrive pas à avoir les bons formats.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void Node::copyTree( Node & original, int kVar[], int kLeaf[], int &kLeafCount, int &leafCount )
    {
            for (int i = 0; i < original.getNchilds(); i++ ){
                if (original.m_children[i]->isLeafNode()){ /* Children are LeafNodes*/
                    LeafNode tmp = new LeafNode(this);
                    tmp.copyTree(original.m_children[i],kVar , kLeaf, kLeafCount, leafCount );
                }
                else { ...
                }
            }
         recalculateCenter();
    }
    Ce qui était * devient *& et les int[] deviennent des int*&.

    Voici le message d'erreur (ligne 6):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    error: no matching function for call to "LeafNode::copyTree(LeafNode*&, int*&, int*&, int&, int&)"
     
    note: candidate is: virtual void LeafNode::copyTree(LeafNode&, int*, int*, int&, int&)
    Est-ce que c'est un problème dans la structure de ma méthode ou s'il existe une solution simple?

  6. #6
    Membre éclairé

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2007
    Messages
    373
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : Royaume-Uni

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Santé

    Informations forums :
    Inscription : Juin 2007
    Messages : 373
    Points : 764
    Points
    764
    Par défaut
    Et avec :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    tmp.copyTree(*original.m_children[i], kVar , kLeaf, kLeafCount, leafCount );
    ... ?

    Ce n'est au fond pas plus compliqué que de lire le message d'erreur et d'appliquer :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    objet = *pointeur
    pointeur = &objet
    Sachant que cette "règle" marche aussi en remplaçant 'objet' par 'référence'.

    Mais typiquement là, dans ton code tu mélanges références et pointeurs sans raison apparente, et ce n'est pas forcément le plus joli à faire.
    L'idéal pour que tu t'en sortes facilement serait de ne travailler qu'avec l'un ou l'autre.

  7. #7
    Futur Membre du Club
    Femme Profil pro
    Étudiant
    Inscrit en
    Août 2011
    Messages
    7
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Août 2011
    Messages : 7
    Points : 5
    Points
    5
    Par défaut
    Citation Envoyé par Kalith Voir le message
    Mais typiquement là, dans ton code tu mélanges références et pointeurs sans raison apparente, et ce n'est pas forcément le plus joli à faire.
    L'idéal pour que tu t'en sortes facilement serait de ne travailler qu'avec l'un ou l'autre.
    En effet... Et c'était là le problème. L'erreur est disparue!
    Malheureusement, une autre est apparue.

    Elle se produit au moment où j'appelle cette fonction:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    tmp.copyTree(original.m_children[i], kVar , kLeaf, kLeafCount, leafCount );
    Avant d'appeler la fonction, je fais un "if" pour valider que original.m_children[i] est un LeafNode. Pourtant, quand j'appelle la méthode LeafNode::copyTree, original.m_children semble avoir oublié qu'il pointe un LeafNode et pas un Node...

    Voici le message d'erreur:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    error: invalid conversion from "Node*" to "LeafNode*"
    error: initializing argument 1 of "virtual void LeafNode::copyTree(LeafNode*, int*, int*, int&, int&)"
    En utilisant typeid() pour avoir une idée de ce qui se passe, je sais que original.m_children est un PNode tandis que *original.m_children est un LeafNode.

    Est-ce que vous sauriez comment gérer ce genre de situation?

  8. #8
    Membre émérite
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 764
    Points : 2 705
    Points
    2 705
    Par défaut
    Ouais, ben là, tu dois passer par du dynamic_cast...
    Il y a sûrement moyen de revoir ta conception pour éviter ça.

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

Discussions similaires

  1. Réponses: 12
    Dernier message: 03/01/2006, 17h23
  2. Je n'y comprend plus rien
    Par Pingouinvert dans le forum Décisions SGBD
    Réponses: 4
    Dernier message: 11/09/2005, 10h57
  3. [BDE] Plus rien dans Configuration/Drivers/Native
    Par Harry dans le forum Bases de données
    Réponses: 5
    Dernier message: 11/02/2005, 16h15
  4. Root qui ne peux plus rien faire :'(
    Par Smortex dans le forum Administration
    Réponses: 2
    Dernier message: 27/09/2004, 20h13
  5. [Kylix] Je n'y comprends plus rien
    Par fafamonteko dans le forum EDI
    Réponses: 5
    Dernier message: 02/03/2004, 16h48

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