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 :

question sur les pointeurs


Sujet :

C++

  1. #1
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2014
    Messages
    521
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2014
    Messages : 521
    Points : 136
    Points
    136
    Par défaut question sur les pointeurs
    Bonjour.

    Je suis débutant en programmation / C++, et je me pose quelques questions...quand on envoi un pointeur d'objet à un autre objet, concrètement, ce qu'on envoi est:

    une simple adresse concernant le début de l'objet en question, donc quelque chose de très légers ?
    ou
    une série d'adresse comprenant toutes les fonctions, attributs etc...de l'objet, donc plus lourd?

    En somme, si j'ai un objet assez chargé en fonction et attributs, dont je n'ai besoin d'avoir accès qu'a quelques attributs/fonctions dans un autre objet, ais-je intérêt à'envoyer l'objet entier en pointeur, ou plutôt de me débrouiller autrement...?

    ex:

    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
     
    class ObjetLourd
    {
    public:
     
         ObjetLourd();
         ~ObjetLourd();
     
         ObjetX renvoiObjetX();
         ObjetY renvoiObjetY();
         etc...
         void faitUneChose(Truck truck, Bidul bidul, float machin);
         etc...
     
         bool renvoiSimple();
     
    private:
     
        ObjetX m_objetX, m_objetX2 etc...;
        ObjetY m_objetY, m_objetY2 etc...;
        etc...
        Truck m_truck1 etc...
        Bidul m_bdul1 etc...  
     
        float m_machin, m_machin2, etc...
     
        bool m_simple;
     
        etc...
     
    }
    a suposé, donc que "ObjetLourd" soit un objet qui contienne lui même plein d'objet, fonctions etc...et que j'ai simplement besoin d'utiliser la fonction bool "renvoiSimple()" par un autre objet


    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
     
     
    class AutreObjet
    {
     
    public:
     
         AutreObjet();
         ~AutreObjet();
     
        void recoitObjetLourd(ObjetLourd *objetLourd)
        {
        m_objetLourd = objetLourd;
        }
     
        void utiliseObjet()
       {
           if(m_objetLourd->renvoiSimple() == true)
           {
               //fait quelqueChose...
           }
       }
     
     
     
    private:
     
        ObjetLourd *m_objetLourd;
    }

    Si je procède ainsi, mon "AutreObjet" prendra-t-il des ressources en plus de facon concéquente ou pas? (même s'il n'utilise pas forcément de facon systématique le pointeur "ObjetLourd"...

    Bref, ce n'est pas encore tout à fait clair pour moi, les pointeurs, voila pourquoi je viens vers vous au cas ou vous pourriez m'aider à y voir plus clair...


    Merci

  2. #2
    Membre émérite
    Avatar de Daïmanu
    Homme Profil pro
    Développeur touche à tout
    Inscrit en
    Janvier 2011
    Messages
    696
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur touche à tout

    Informations forums :
    Inscription : Janvier 2011
    Messages : 696
    Points : 2 435
    Points
    2 435
    Par défaut
    Bonjour.

    Dans ton exécutable, la règle est simple : « Tout est une question adresse » (les pointeurs, les fonctions, les variables etc…)

    Quant tu transmet un pointeur à quelqu'un d'autre, tu lui transmet uniquement une adresse, un emplacement en mémoire RAM.

    Maintenant comment le destinataire sait où se trouve le reste ? Où se cache faitUneChose(Truck, Bidul,float) en mémoire ?
    Comme je l'ai dit précédemment, tout est adresse, y compris les fonctions.

    Quand dans le code source on a objet.faitUneChose(t, b, f);, le compilateur inscrit l'adresse de la fonction « en dur » dans l’exécutable (ou plutôt un offset mais là on rentre dans le très bas niveau).
    Je fais appel aux esprits de Ritchie, Kernighan, Stroustrup et Alexandrescu
    Donnez moi la force, donnez moi le courage de coder proprement !

    « Ça marche pas » n'est PAS une réponse convenable, merci de détailler le souci en fournissant l’environnement, le code source, les commandes et les messages d'erreur.

    Ce club possède également un clavardage, on y trouve quelques perles entre deux sessions d'entraides.

  3. #3
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2014
    Messages
    521
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2014
    Messages : 521
    Points : 136
    Points
    136
    Par défaut
    Merci pour votre réponse, Daïmanu.

    Si j'ai bien saisi, donc, quand j'envoi un pointer d'objet à un objet, en fait, je lui envoi toutes les adresses de chacune des fonctions de l'objet? donc plus l'objet envoyé est lourd, plus le pointeur envoyé le sera également, et donc l'objet qui le recevra aussi ?

  4. #4
    Membre émérite
    Avatar de Daïmanu
    Homme Profil pro
    Développeur touche à tout
    Inscrit en
    Janvier 2011
    Messages
    696
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur touche à tout

    Informations forums :
    Inscription : Janvier 2011
    Messages : 696
    Points : 2 435
    Points
    2 435
    Par défaut
    Le pointeur c'est simplement une adresse mémoire, rien de plus. Sa taille ne varie pas selon ce vers quoi il pointe.

    Après c'est le compilateur qui gère tout ce qui est adresse, taille etc. On a pas a s'y préoccuper, c'est totalement transparent.
    Je fais appel aux esprits de Ritchie, Kernighan, Stroustrup et Alexandrescu
    Donnez moi la force, donnez moi le courage de coder proprement !

    « Ça marche pas » n'est PAS une réponse convenable, merci de détailler le souci en fournissant l’environnement, le code source, les commandes et les messages d'erreur.

    Ce club possède également un clavardage, on y trouve quelques perles entre deux sessions d'entraides.

  5. #5
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2014
    Messages
    521
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2014
    Messages : 521
    Points : 136
    Points
    136
    Par défaut
    OK.

    Donc, que j'envoi le pointeur d'un objet "mégaLourd", ou celui d'un "SuperLégers", il aura le même poid, prendra la même place (soit un simple nombre correspondant à une adresse).
    C'est simplement quand je l'utilise, qu'il va parcourir toutes les fonctions/attributs de l'objet concerné?

    Si j'ai bien compris, j'ai donc tout intérêt a envoyé des pointeurs d'objet, même lourd, pour une questions d'organisations du code, car cela ne ralentira pas plus le programme...?

  6. #6
    Membre émérite
    Avatar de Daïmanu
    Homme Profil pro
    Développeur touche à tout
    Inscrit en
    Janvier 2011
    Messages
    696
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Alpes Maritimes (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur touche à tout

    Informations forums :
    Inscription : Janvier 2011
    Messages : 696
    Points : 2 435
    Points
    2 435
    Par défaut
    Citation Envoyé par mazertys17 Voir le message
    Si j'ai bien compris, j'ai donc tout intérêt a envoyé des pointeurs d'objet, même lourd, pour une questions d'organisations du code, car cela ne ralentira pas plus le programme...?
    Quand tu dois transmettre un objet à une fonction, tu a deux choix : soit tu passe par valeur, soit par pointeur / référence
    Par valeur void f(ObjetLourd o); tu envoie une copie de ton objet à la fonction. Une copie d'autant plus longue à faire que ton objet est lourd.
    Par pointeur void f(ObjetLourd *o); tu envoie une copie de l'adresse de l'objet à la fonction, ce qui est bien plus léger. Les références f(ObjetLourd& o); sont un peu différents mais tout aussi rapide.

    Mais attention à ne pas tomber dans le piège « Mettre des pointeurs partout », il faut en mettre uniquement quand on doit transmettre un objet.
    Je fais appel aux esprits de Ritchie, Kernighan, Stroustrup et Alexandrescu
    Donnez moi la force, donnez moi le courage de coder proprement !

    « Ça marche pas » n'est PAS une réponse convenable, merci de détailler le souci en fournissant l’environnement, le code source, les commandes et les messages d'erreur.

    Ce club possède également un clavardage, on y trouve quelques perles entre deux sessions d'entraides.

  7. #7
    Membre éprouvé
    Profil pro
    Inscrit en
    Juillet 2009
    Messages
    307
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2009
    Messages : 307
    Points : 983
    Points
    983
    Par défaut
    Citation Envoyé par mazertys17 Voir le message
    OK.

    Donc, que j'envoi le pointeur d'un objet "mégaLourd", ou celui d'un "SuperLégers", il aura le même poid, prendra la même place (soit un simple nombre correspondant à une adresse).
    C'est simplement quand je l'utilise, qu'il va parcourir toutes les fonctions/attributs de l'objet concerné?

    Si j'ai bien compris, j'ai donc tout intérêt a envoyé des pointeurs d'objet, même lourd, pour une questions d'organisations du code, car cela ne ralentira pas plus le programme...?
    La taille des pointeurs c'est sizeof(void *) souvent 4 ou 8 octets. Quand tu as un objet lourd situé à l'adresse x alors si tu accedes à une variable membre m_ma_variable alors le compilateur sait que ce membre est situé à l'adresse x + offset où offset depend du membre bref c'est du temps constant. Si tu appelles une fonction virtuelle alors il y a simplement une indirection en plus mais c'est rapide.

    ps: utilise plutot des references "&" qui ne peuvent etre null pour passer tes objets "lourds"

  8. #8
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2014
    Messages
    521
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2014
    Messages : 521
    Points : 136
    Points
    136
    Par défaut
    Ok.

    Je vais donc faire les choses comme ça, ce qui simplifiera l'utilisation de mon code.

    Merci à vous, merci à Developpez

  9. #9
    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 704
    Points
    2 704
    Par défaut
    Je vais formuler une réponse à ma manière.

    Quand tu envoies un pointeur, tu n'envoies effectivement qu'une adresse, dont la taille dépend uniquement de la plate-forme, et pas de l'objet pointé.
    C'est donc très majoritairement la manière la plus efficace de passer des objets, sauf pour les très petits types (char, par exemple).

    Ensuite, comment sont donc déterminé les adresses des attributs appartenant à cet objet ?

    Cela se fait grâce au typage du pointeur. C'est l'information sur le type qui permet de retrouver les membres uniquement à partir de l'adresse de début de l'objet.

    Si tu passes un objet en void*, tu ne pourras pas en faire grand chose, car il lui manquera cette information de type. À moins que tu ne le fasses passer dans un static_cast.

  10. #10
    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 518
    Points
    41 518
    Par défaut
    En C++, il existe des pointeurs plus lourds que d'autres, mais seulement pour un cas précis: Les pointeurs de membre vers impliquant des classes liées à de l'héritage multiple ou virtuel.

    Tous les pointeurs "ordinaires" vers des objets ou des variables simples sont légers, juste l'adresse de l'objet.

    Je sens que tu te poses des questions pour les appels de fonctions membres d'un objet via un pointeur de celui-ci:
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    MaClasse * pObjet = FonctionQuiRetourneUnPointeur();
    pObjet->FonctionMembre("toto");
    pObjet->FonctionMembreVirtuelle(42);
    Le premier cas est simple, car la fonction void MaClasse::FonctionMembre(char const*) est en fait sous le capot une fonction void MaClasse_FonctionMembre(MaClasse*, char const *). Donc en fait, c'est juste l'équivalent d'un appel de fonction normal.

    Pour le second cas, il va en fait être question de pointeurs de fonctions. Dès que tu déclares une fonction virtuelle dans MaClasse, elle va gagner une variable membre "secrète" qui est un pointeur vers sa table des fonctions virtuelles, aussi appelée vtable. Celle-ci peut être considérée comme une structure globale dont les membres sont des pointeurs de fonctions:
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class MaClasse
    {
    public:
    	int variableMembre;
     
    	void FonctionMembre(char const *);
    	virtual void FonctionMembreVirtuelle(int);
    };
     
    class ClasseDerivee : public MaClasse
    {
    	void FonctionMembreVirtuelle(int); //redéfinition de la fonction
    };
    Correspond en fait à un truc de ce genre:
    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
    class MaClasse
    {
    public:
    	struct MaClasseVtable *__pVtable;
    	int variableMembre;
    };
     
    struct MaClasseVtable
    {
    	void (*pFonctionMembreVirtuelle)(MaClasse*, int);
    };
     
    void MaClasse_FonctionMembre(MaClasse*, char const *);
    void __MaClasse_FonctionMembreVirtuelle(MaClasse*, int);
     
    struct MaClasseVtable *__MaClasse_vtable = {
    	__MaClasse_FonctionMembreVirtuelle
    };
     
     
    class ClasseDerivee : MaClasse
    {
    };
     
    //ClasseDerivee a sa propre version de FonctionMembreVirtuelle()
    void __ClasseDerivee_FonctionMembreVirtuelle(ClasseDerivee*, int);
     
    //ClasseDerivee a sa propre vtable qui contient les pointeurs vers sa propre version des fonctions virtuelles.
    struct MaClasseVtable  *__ClasseDerivee_vtable = {
    	(void (*)(MaClasse*, int)) __ClasseDerivee_FonctionMembreVirtuelle
    };
    Ainsi, chaque instance de la classe possède un pointeur vers sa vtable: Les objets créés avec new MaClasse pointeront vers la vtable de MaClasse, ceux créés avec new ClasseDerivee pointeront vers la vtable de ClasseDerivee.

    Et les appels dans tout ça? Simple: pObjet->FonctionMembreVirtuelle(42); devient juste pObjet->__pVtable->pFonctionMembreVirtuelle(pObjet, 42);Dans tous les cas le pointeur reste léger, c'est jusque les appels virtuels ont deux niveaux d'indirection en plus.
    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.

  11. #11
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2014
    Messages
    521
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2014
    Messages : 521
    Points : 136
    Points
    136
    Par défaut
    Ok .

    Merci pour vos réponses. C'est encore un peut compliquer pour moi, mais ça me donne des pistes !
    Ce qui me parait clair, en tout cas, c'est qu'on peu utiliser des pointeurs sans modération, ce qui est bien pratique!
    J'aurais d'autres questions, mais plus sur les pointeurs alors je vais ouvrir un autre topic..

    Merci en tout cas !

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Salut,
    Citation Envoyé par mazertys17 Voir le message
    Ok .

    Merci pour vos réponses. C'est encore un peut compliquer pour moi, mais ça me donne des pistes !
    Ce qui me parait clair, en tout cas, c'est qu'on peu utiliser des pointeurs sans modération, ce qui est bien pratique!
    Surtout pas, malheureux!!!

    Les pointeurs, c'est une véritable plaie, ne serait-ce que parce qu'il faut être particulièrement attentif à s'assurer que l'adresse renvoyée / reçue corresponde bel et bien à l'adresse mémoire à laquelle on pourra trouver un objet du type attendu.

    De plus, la notion de pointeur est très fortement corrélée à la notion de gestion dynamique de la mémoire, ce qui rajoute son lot de problèmes allant de la fuite mémoire à la tentative de double libération de celle-ci.

    Mais le principal problème, c'est qu'un pointeur peut représenter une adresse mémoire "connue pour être invalide" (ou si tu préfères : connue pour ne pas correspondre à une adresse à laquelle tu trouveras effectivement un objet du type attendu), et qu'il t'appartient de veiller
    1. à t'assurer que ton pointeur ne présente pas cette valeur invalide avant d'essayer d'accéder à l'objet auquel tu penses accéder
    2. de veiller à modifier la valeur de ton pointeur pour lui donner cette valeur invalide lorsque l'objet pointé cesse d'exister


    Par contre, les références présentent presque toutes les caractéristiques des pointeurs (lorsque l'on regarde le code binaire généré, une référence est en réalité traitée exactement comme un pointeur), avec deux particularités :
    1. l'objet référencé doit impérativement exister et
    2. les références ne présentent pas cette corrélation avec la gestion dynamique de la mémoire

    Si tu veux user et abuser de quelque chose, entre autres pour éviter les copies inutiles, c'est des références, éventuellement constantes, car tu devrais limiter l'utilisation des pointeurs (que tu devrais prendre soin de rendre intelligents, si tu disposes de C++11) au seul cas (finalement assez rare) où "la donnée peut ne pas exister"
    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

  13. #13
    Membre expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Points : 3 156
    Points
    3 156
    Par défaut
    Merci Koala, je me demandais quand est-ce que quelqu'un allait enfin conseiller à l'op d'utiliser des références ! La notion de référence est un peu plus difficile à apprendre que celle de pointeur pour le débutant, mais est indispensable pour écrire du code robuste.
    Find me on github

  14. #14
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

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

    Informations forums :
    Inscription : Juin 2011
    Messages : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    Citation Envoyé par jblecanard Voir le message
    La notion de référence est un peu plus difficile à apprendre que celle de pointeur pour le débutant
    ?? Je suis curieux de savoir pourquoi ?

    Pour moi une référence n'a que 2 règles: elle doit être initialisée et toute manipulation sur la référence revient à manipuler la variable qui à servit à l'initialisation.

    Au contrario, un pointeur contient l'adresse d'une variable ; peux changer en cours de route ; peut contenir une adresse qui n'est pas valide ; représente des fois un segment d'élément et des fois non ; il faut le déréférencer pour utiliser la valeur ; les tableaux peuvent se transformer en pointeurs, un int[n][m] n'est pas un int** ; etc (plus d'idées :p).

    Je pense que le problème vient de l'enseignement des références sur la base des pointeurs. Le fameux "une référence est un pointeur constant non nul".

  15. #15
    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 518
    Points
    41 518
    Par défaut
    C'est peut-être lié au facteur qui avait causé mon dégoût initial des références: La différence de "lisibilité" entre int a=0; foo(&a); et int a=0; foo(a); fait paraître les références plus "sournoises" que les pointeurs (hormis les références constantes, bien sûr).

    Il y a aussi des implications non pas liées au langage, mais aux pratiques, quant à la durée de vie: Quand une fonction autre qu'un constructeur accepte un paramètre par référence, personne ne s'attend à ce que la référence persiste après la fin de l'exécution de la fonction; il en est différemment pour les pointeurs (du moins, jusqu'à maintenant; les codeurs modernes employant les pointeurs intelligents tendent à appliquer le même raisonnement aux pointeurs nus).
    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.

  16. #16
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Yvelines (Île de France)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Points : 16 213
    Points
    16 213
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    C'est peut-être lié au facteur qui avait causé mon dégoût initial des références: La différence de "lisibilité" entre int a=0; foo(&a); et int a=0; foo(a); fait paraître les références plus "sournoises" que les pointeurs (hormis les références constantes, bien sûr).
    Je pense que c'est avant tout une histoire de documentation. Effectivement, une fonction nommée foo qui modifierait son paramètre serait néfaste. Mais dans la vraie vie, à chaque fois que j'ai vu une fonction utiliser des références non constantes, elle avait un nom ou une série de paramètre qui faisait qu'il n'y avait pas d'ambiguïté. Et puis, avec l'avènement de la move semantic, on utilise moins le passage d'arguments par référence non constante, mais plus la valeur de retour, ce qui est une bonne chose pour d'autres raisons aussi, en particulier la possibilité d’initialiser une variable.
    Citation Envoyé par Médinoc Voir le message
    Il y a aussi des implications non pas liées au langage, mais aux pratiques, quant à la durée de vie: Quand une fonction autre qu'un constructeur accepte un paramètre par référence, personne ne s'attend à ce que la référence persiste après la fin de l'exécution de la fonction; il en est différemment pour les pointeurs (du moins, jusqu'à maintenant; les codeurs modernes employant les pointeurs intelligents tendent à appliquer le même raisonnement aux pointeurs nus).
    Tu pourrais préciser ton raisonnement ?
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  17. #17
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2014
    Messages
    521
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2014
    Messages : 521
    Points : 136
    Points
    136
    Par défaut
    Je vois que les pointeurs suscitent des débats !

    Dans mon cas, je pars d'objets normaux, crée dans une "page" "NiveauX", qui envoie leurs références (objetQuelconque.recoitObjet(&monObjet) ), qui sont ensuite "transformés" en pointeurs, (de ce qu'il me semble en tout cas) pour être utilisés par diverses autres objets...(monObjet->faitCa())
    Mon programme est assez simple, donc j’espère ne pas avoir de soucis quant à leurs utilisations...plus tard...car pour l'instant ça marche bien et c'est vraiment très pratique.

    Merci pour vos réponses.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Donc, en gros, tu as une fonction dont le prototype ressemble àvoid MonTypeDobjet::recoitObjet(MonTypeDobjet * param); c'est bien ca?

    Maintentant, pose toi deux questions :
    • Que se passe-t-il si l'objet n'existe pas(autrement dit, param peut il valoir NULL (nullptr en C++11) Est-ce que cela peut seulement arriver
    • Est-ce que la fonction doit pouvoir modifier param

    Car, lorsque tu passes un paramètre sous forme de pointeur, tu dois forcément t'assurer que le pointeur pointe vers une adresse (dont on espère) qu'elle sera valide. Je veux dire par là que l'adresse doit non seulement être valide (différente de NULL / nullptr), mais aussi qu'il faut que l'on ait la certitude que ce qui s'y trouve est bel et bien un objet du type auquel on s'attend (voire un objet d'un type pouvant "passer pour être" du type auquel on s'attend, pour profiter du polymorphisme d'inclusion grâce à l'héritage public).

    Tant que tu te contente de prendre l'adresse d'un objet créé sur la pile, tu peux avoir ce genre de certitude. Mais dés que tu vas commencer à avoir recours à l'allocation dynamique de la mémoire -- et tu y arriveras forcément une fois que tu commencera à utiliser l'héritage public -- il en ira différemment, car cette certitude est conditionnée par le fait que l'objet pointé par le pointeur n'ait pas encore été détruit... Et le problème est que, parfois, on ne se rend pas forcément compte que l'on passe par un point du code sur lequel on appelle delete (simplement à cause d'une logique trop complexe).

    On peut toujours éviter certains problèmes -- à condition de veiller à remettre les pointeurs vers des objets détruits à NULL/nullptr -- en veillant à placer de manière systématique l'accès à l'objet pointé dans un test voire en plaçant une assertion (l'assertion permet de se rendre compte de son erreur lors du test, mais cela implique de tester systématiquement tous les chemin qui mènent à l'appel de la fonction). C'est à dire que le corps de ta fonction prendra systématiquement une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    void MonTypeDobjet::recoitObjet(MonTypeDobjet * param){
        assert(param!=nullptr);
        if(param){
            param->faitCa();
        }
    }
    Ne pas prendre ce genre de précaution revient systématiquement à te tirer une balle dans le pied. Et, même en agissant de la sorte, cela ne te garanti pas forcément que tu n'auras pas de problème. Le pire de l'histoire, c'est que l'origine réelle des problèmes peut se trouver parfois très loin de l'appel de la fonction

    De plus, les règles pour assurer la constance avec un pointeur sont particulières, car on peut assurer la constance de l'adresse (le pointeur ne peut pas prendre une autre adresse que celle qu'elle a) ou de l'objet (on ne peut pas modifier l'objet pointé), voir des deux... C'est assez surprenant d'avoir à écrire un code comme const Type const * myPtr pour s'assurer que l'on n'essayera pas de modifier l'objet pointé par le pointeur ni de modifier l'adresse représentée par ce pointeur (pour lui donner l'adresse d'un autre objet), mais c'est pourtant ce qu'il faudrait faire dans certains cas

    Par contre, les références, c'est autre chose... Car une référence n'est qu'un alias de l'objet référencé et que, tout comme ton pseudo sur ce forum, une référence est irrémédiablement associée à l'objet référencé :
    • c'est parce que tu existes physiquement (sous un autre nom) que tu a pu créer ton pseudo mazertys17 et
    • le pseudo mazertys17 ne correspondra jamais qu'à toi, et à personne d'autre

    Mieux encore, si tu déclares une référence constante, tu as la certitude que la référence t'interdira toute modification de l'objet référencé. Au final, si tu n'as pas une bonne raison de croire que le paramètre de ta fonction puisse ne pas exister, tout devrait t'inciter à utiliser une référence (éventuellement constante, si la fonction ne doit pas modifier l'état du paramètre) car elle pourrait prendre la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void MonTypeDobjet::recoitObjet(MonTypeDobjet /*const*/ & param){
       //plus besoin d'assertion ni de test : l'objet référencé par param existe forcément
       /* En plus, ce qui ne gâche rien, tu utilise le paramètre exactement comme s'il s'agissait 
        * de l'objet "original".  En ce, y compris la syntaxe d'accès à ses membres
        */
        param.faitCa();
    }
    Et l'appel de la fonction en est -- lui aussi simplifié, car cela peut prendre la forme de objetQuelconque.recoitObjet(monObjet).

    Pour conclure, je vais même aller plus loin encore : Tu ne pourras pas éviter le recours à l'allocation dynamique de la mémoire dans tes développements, et, partant, tu ne pourras pas éviter de manipuler des pointeurs. Mais, même si tu maintiens tes objets sous la forme de pointeur "quelque part" (par exemple, dans une collection d'objets polymorphes), lorsqu'il est question de passer ces objets en paramètre de fonction et que tu n'as aucune raison de croire que le paramètre reçu par la fonction puisse ne pas exister (ou de croire que ta fonction puisse prendre une décision relative à la durée de vie de son paramètre), tu as largement intérêt à transmettre le paramètre sous forme de référence (éventuellement constante), quitte à transmettre "ce qui est pointé par le pointeur" plutôt que de transmettre le paramètre sous forme de pointeur.

    Cela t'évitera bien des arrachages de cheveux
    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

  19. #19
    Membre habitué
    Homme Profil pro
    Étudiant
    Inscrit en
    Octobre 2014
    Messages
    521
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Octobre 2014
    Messages : 521
    Points : 136
    Points
    136
    Par défaut
    Donc, en gros, tu as une fonction dont le prototype ressemble àvoid MonTypeDobjet::recoitObjet(MonTypeDobjet * param); c'est bien ca?
    Oui, exactement.

    Maintentant, pose toi deux questions :
    Que se passe-t-il si l'objet n'existe pas(autrement dit, param peut il valoir NULL (nullptr en C++11) Est-ce que cela peut seulement arriver
    Normalement, non...En fait je crée les objet en dur depuis un objet qui va controler tous les objets présent dans le niveau.
    (sauf pour les objets qui sont voués a disparaître, en effet )

    Est-ce que la fonction doit pouvoir modifier param
    Absolument, c'est d'ailleurs l'objectif.


    Mais dés que tu vas commencer à avoir recours à l'allocation dynamique de la mémoire -- et tu y arriveras forcément une fois que tu commencera à utiliser l'héritage public
    En fait, j'en suis arrivé a me dire que je n'allais pas utilisé le concept d'héritage, bien que pratique. Je crois avoir vu quelque part que ca pouvait générer des problèmes, surtout avec les copies d'objets...Mon programme étant simple, faire plus de code ne m'effraye pas...Par contre, j'utilise bien sur l'allocation dynamique, et là, en effet, avec les pointeurs, ça complique.(si on parle bien de la même chose)



    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    void MonTypeDobjet::recoitObjet(MonTypeDobjet * param){
        assert(param!=nullptr);
        if(param){
            param->faitCa();
        }
    }
    Merci pour l'info, ça risque de m'être bien utile

    De plus, les règles pour assurer la constance avec un pointeur sont particulières, car on peut assurer la constance de l'adresse (le pointeur ne peut pas prendre une autre adresse que celle qu'elle a) ou de l'objet (on ne peut pas modifier l'objet pointé), voir des deux... C'est assez surprenant d'avoir à écrire un code comme const Type const * myPtr pour s'assurer que l'on n'essayera pas de modifier l'objet pointé par le pointeur ni de modifier l'adresse représentée par ce pointeur (pour lui donner l'adresse d'un autre objet), mais c'est pourtant ce qu'il faudrait faire dans certains cas

    J'ai eu des choses étonantes...Comme, par exemple être obligé de renvoyer une 2ème fois le pointeur a un objet, car il semblai que l'adress avait changé, sans pour autant, l'avoir demandé, ce qui est embêtant.
    Comment peut-on faire pour s'assurer que l'adresse du pointeur reste toujours la même dès qu'un objet est crée , tout en pouvant avoir accès à ses fonctions?


    tu as largement intérêt à transmettre le paramètre sous forme de référence
    C'est en effet ce que je fais au maximum...

    Merci pour toutes ces réponses, ça m'éclaire toujours un peu plus !

  20. #20
    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 518
    Points
    41 518
    Par défaut
    Un objet en C++ ne change pas d'adresse.
    Par contre, son contenu peut être copié ou transféré (surtout en C++11) dans un autre.

    Quand on veut un objet qui ne bouge pas (c'est généralement pour une utilisation avec virtualité) il peut être intéressant de le rendre non-copiable (surtout s'il est dans une hiérarchie de classes, où il risquerait un problème de slicing).
    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.

Discussions similaires

  1. Question sur les pointeurs génériques
    Par mikedavem dans le forum C
    Réponses: 16
    Dernier message: 24/05/2006, 11h56
  2. question sur les pointeurs
    Par jd.baculard dans le forum Langage
    Réponses: 3
    Dernier message: 18/03/2006, 02h30
  3. [Debutant] Nouvelle question sur les pointeurs
    Par etiennegaloup dans le forum Débuter
    Réponses: 3
    Dernier message: 11/01/2006, 09h55
  4. Question sur les pointeurs.
    Par Chrisemi dans le forum C++
    Réponses: 5
    Dernier message: 28/10/2005, 23h47
  5. questions sur les pointeurs
    Par Hyoga dans le forum C++
    Réponses: 17
    Dernier message: 08/01/2005, 23h25

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