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 :

Surcharge d'opérateurs


Sujet :

C++

  1. #21
    Expert éminent sénior
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 630
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 630
    Points : 10 556
    Points
    10 556
    Par défaut
    Effectivement il y a 1 void en trop ... mais tu peux le mettre entre parenthèse pour dire que tu ne veux pas du retour <- à vérifier pour effacer les warnings.

    void laFonctionQuiModifieUneCopie(Point point), appel laFonctionQuiModifieUneCopie(p) : tu appelles par valeur, paramètre entrée mais tu as 1 copie
    void laFonctionQuiModifieUneCopie(Point& point), appel laFonctionQuiModifieUneCopie(p) : paramètre entrée/ sortie ou sortie
    void laFonctionQuiModifieUneCopie(Point* point), appel laFonctionQuiModifieUneCopie(&p) : paramètre entrée/ sortie ou sortie
    Point laFonctionQuiModifieUneCopie(const Point& point), appel laFonctionQuiModifieUneCopie(p) : paramètre entrée mais peut-être appel de l'optimisation RVO (Return Value Optimization)

  2. #22
    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
    Ce qu'il faut comprendre, c'est que tu pourrais avoir deux fonctions qui ressembleraient à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    void laFonctionQuiModifieUneReference(Point & p){
        // modification du point "en interne"
    }
    ou à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Point laFonctionQuiModifieUneCopie(Point p){
        // modification du point
        return p; // renvoie le point modifié
    }
    Si je fais appel à la première fonction sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    int main(){
        Point lePoint;
        laFonctionQuiModifieUneReference(lePoint);
        /* ... */
    }
    j'obtiendrai au final (comprends: après l'exécution de la fonction) exactement le même résultat que si je fais appel à la deuxième fonction sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    int main(){
        Point lePoint;
        lePoint =  laFonctionQuiModifieUneCopie(lePoint);
        /* ... */
    }
    La différence porte sur la manière dont le résultat est obtenu:

    La première fonction va travailler avec une référence (d'où l'esperluette entre Point et p): on signale que p est destiné à agir comme "un alias" de lePoint.

    A titre de comparaison, nous pourrions dire que p est l'équivalent de mon pseudo (koala01) sur le forum: Quand tu vois une réponse postée par "koala01", c'est en fait ... moi (Philippe Dunski) qui l'ai écrite.

    Si tu viens à insulter ou à congratuler "koala01", l'insulte ou la congratulation s'adressera toujours à une personne "bien vivante": moi (Philippe Dunski).

    Hé bien, le passage par référence agit exactement de la même manière: si la fonction appelée (AKA laFonctionQuiModifieUneReference) manipule un point qu'elle connait comme étant nommé p, il faut savoir que c'est en fait la donnée lePoint, qui existe au niveau de la fonction appelante, qui sera modifiée.

    A l'inverse, lorsque le paramètre est transmis par valeur, il n'y a aucun lien particulier entre la donnée p -- qui est la donnée manipulée par laFonctionQuiModifieUneCopie -- et la donnée qui a été transmise en paramètre lorsque la fonction a été appelée (la donnée lePoint).

    A ceci près, quand même, que p est une copie de lePoint, et que les deux peuvent dont être modifiés de manière indépendante.

    Et comme p va cesser d'exister une fois que l'exécution de la fonction sera achevée, si on veut récupérer les modification apportées par la fonction, il faut ... renvoyer le résultat modifié. Et bien sur, veiller à récupérer ce résultat modifié dans la variable lePoint si l'on souhaite que lePoint "subisse" les modifications qui auront été apportées à p.

    L'usage d'une fonction n'est pas précédé pas le type de renvoi normalement !
    En effet... c'est bel et bien une erreur de ma part (je la corrige immédiatement ).

    Ce qui n'empêche que le code reste malgré tout parfaitement correct et valide dans le cadre de l'appel d'une fonction
    D'autre part, si cette fonction modifie une référence, elle doit prendre une référence (ou un pointeur) en paramètre, hors p est une variable de type Point, le paramètre devrait dont être présenté à la fonction précédé du &.
    Héhé ... non...

    Le problème est que l'esperluette (&) représente plusieurs choses en fonction du contexte dans lequel il est utilisé.
    • Lorsque l'esperluette se trouve entre un type de donnée et l'identifiant d'une donnée du type indiqué, nous avons affaire à une déclaration et elle représente le fait que la donnée sera traitée comme étant une référence.
    • Lorsque l'esperluette n'est pas précédée d'un type de donnée, nous sommes alors dans le cadre de l'accès à la donnée indiquée et elle représente l'opérateur "address of", c'est à dire l'opérateur qui permet de récupérer l'adresse mémoire à laquelle la donnée se trouve actuellement.



    Il y a donc plusieurs circonstances à distinguer:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    void foo(Point & p){
        /* ... */
    }
    Point & p correspond à la ... déclaration d'un paramètre qui sera "une référence sur un point" et auquel nous pourrons accéder au travers de l'identifiant p.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    Point lePoint;
    Point & laRef = lePoint;
    Point & laRef indique laRef comme étant une "référence sur un point" à laquelle est associée la donnée originale lePoint;

    NOTA: les références doivent systématiquement être définies, c'est à dire qu'il faut impérativement indiquer quelle est la "donnée d'origine" associée à une référence.

    NOTA: Une fois que la "donnée d'origine" a été associée à une référence, il devient impossible d'y associer une autre donnée pour toute la durée de vie de la référence. Il ne serait donc pas possible, après avoir associé lePoint à laRef (ce qui est indispensable), de faire en sorte d'utiliser laRef pour référencer une éventuelle donnée autrePoint.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int main(){
        Point lePoint;
        foo(&lePoint);
    }
    &lePoint agit comme un accès à la donnée nommée lePoint afin de faire appel à la fonction foo. L'esperluette va donc être interprétée comme étant l'opérateur "address of", si bien que ce sera ... l'adresse mémoire à laquelle se trouve effectivement lePoint (autrement dit, un pointeur sur la donnée lePoint) qui sera transmise à foo
    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

  3. #23
    Membre du Club
    Homme Profil pro
    Responsable de compte
    Inscrit en
    Juin 2014
    Messages
    215
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Pyrénées Orientales (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Responsable de compte

    Informations forums :
    Inscription : Juin 2014
    Messages : 215
    Points : 60
    Points
    60
    Par défaut
    Je pense que c'est plus clair.

    A partir de cette partie, c'est plus trouble.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    Point lePoint;
    Point & laRef = lePoint;
    J'ai pu tester que si je modifie lePoint, laRef n'est pas modifié. (même adresse pour lePoint et laRef, &lePoint et &laRef indiquent une seule et même adresse.

    Par contre, avec ce code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    Point lePoint;
    Point laRef = lePoint;
    j'ai pu tester que si je modifie lePoint, laRef est aussi modifié (les modifications faites sur le point se répercutent sur laRef) et &lePoint et &laRef indiquent 2 adresses distinctes.


    Sinon, cette partie
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int main(){
        Point lePoint;
        foo(&lePoint);
    }
    me reste obscure et ne compile pas chez moi, ce qui ne me surprend pas trop puisque foo attend une variable en paramètre alors qu'on lui donne une référence !

  4. #24
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Je me demande bien ce que tu as testé parce que ce que tu racontes est totalement faux.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Point lePoint;
    Point & laRef = lePoint;
    laRef et lePoint sont la même variable. Changer l'un va changer l'autre.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Point lePoint;
    Point laRef = lePoint;
    Ce sont 2 variables totalement indépendantes, donc évidemment changer l'une ne va avoir absolument aucun impact sur l'autre.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int main(){
        Point lePoint;
        foo(&lePoint);
    }
    [...] foo attend une variable en paramètre alors qu'on lui donne une référence !
    Une référence est une variable, et ton vocabulaire est mauvais. La syntaxe & prend l'adresse d'une variable qui est représentée par la notation pointeur.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  5. #25
    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 hary66 Voir le message
    Je pense que c'est plus clair.

    A partir de cette partie, c'est plus trouble.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    Point lePoint;
    Point & laRef = lePoint;
    J'ai pu tester que si je modifie lePoint, laRef n'est pas modifié. (même adresse pour lePoint et laRef, &lePoint et &laRef indiquent une seule et même adresse.
    Tu as donc du faire une c...rie, car je peux t'assurer qu'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
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    #include <iostream>
    /* j'utilise une structure car cela permettra de modifier plus facilement les données de bases ;) */
    struct Point{
        Point():Point{0,0,0}{
        }
        Point(int x, int y, int z):x{x},y{y},z{z}{
        }
        int x;
        int y;
        int z;
    };
    /* surcharge de l'opérateur << adaptée à un Point */
    std::ostream & operator <<(std::ostream & os, Point const & p){
        os<<"x : "<<p.x<<"\n"
          <<"y : "<<p.y<<"\n"
          <<"z : "<<p.z<<"\n";
        return os;
    }
    int main(){
        /* Créons un point dont les valeurs sont 
         * x = 1
         * y = 2
         * z = 3
         */
        Point lePoint{1,2,3};
        std::cout<<"lePoint affiche sans surprise\n"
                 <<lePoint;
        Point & laRef = lePoint;
        std::cout<<"laRef présente les valeurs de lePoint\n"
                 <<laRef;
        /* Modifions les valeurs de lePoint : */
        lePoint.x *=2;
        lePoint.y *=2;
        lePoint.z *=2;
        std::cout<<"toutes les donnees de lePoint changent:\n"
                 <<lePoint;
        std::cout<<"qu'en est-il de laRef?\n"
                 <<laRef;
        /* modifions laRef pour qu'elle corresponde à 
         * x = 10
         * y = 12
         * z = 15
         */
        laRef.x = 10;
        laRef.y = 12;
        laRef.z = 15;
        std::cout<<"les valeurs de laRef changent\n"
                 <<laRef;
        std::cout<<"Qu'en est il de lePoint?\n"
                 <<lePoint;
        return 0;
    }
    donnera un résultat équivalent à

    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
    lePoint affiche sans surprise
    x : 1
    y : 2
    z : 3
    laRef présente les valeurs de lePoint
    x : 1
    y : 2
    z : 3
    toutes les donnees de lePoint changent:
    x : 2
    y : 4
    z : 6
    qu'en est-il de laRef?
    x : 2
    y : 4
    z : 6
    les valeurs de laRef changent
    x : 10
    y : 12
    z : 15
    Qu'en est il de lePoint?
    x : 10
    y : 12
    z : 15
    La preuve ==>ICI<==.

    Par contre, avec ce code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    Point lePoint;
    Point laRef = lePoint;
    j'ai pu tester que si je modifie lePoint, laRef est aussi modifié (les modifications faites sur le point se répercutent sur laRef) et &lePoint et &laRef indiquent 2 adresses distinctes.
    Absolument faux, car la donnée laRef n'est pas une référence ici, mais simplement une variable "tout ce qu'il y a de plus normal", si ce n'est qu'on lui aura donné, à la base, les même valeurs que pour lePoint

    LaRef et LePoint sont donc deux variables qui vont évoluer de manière totalement indépendante: Si tu modifie laRef, il n'y a aucune raison pour que lePoint soit modifié en retour, et inversement.

    Sinon, cette partie
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int main(){
        Point lePoint;
        foo(&lePoint);
    }
    me reste obscure et ne compile pas chez moi,
    Il n'était pas sensé compiler non plus, vu qu'il n'a absolument rien à voir avec les deux morceaux de code qui le précèdent.

    Il faut prendre l'habitude de lire correctement -- au besoin plusieurs fois -- les réponses qui te sont données, parce que chaque mot compte!

    J'avais cru qu'il était clair, après avoir précisé que
    Il y a donc plusieurs circonstances à distinguer:
    que j'allais donner trois exemples qui n'ont rien à voir les uns avec avec les autres, mais, comme dit mon vénéré père: il faut laisser croire les béguines, elles sont spécialistes et payées pour

    Toujours est-il que ces trois codes sont parfaitement corrects, si ce n'est qu'ils sont tous les trois sortis de leur contexte et qu'il faudra donc les adapter avant de pouvoir en faire quoi que ce soit.
    ce qui ne me surprend pas trop puisque foo attend une variable en paramètre alors qu'on lui donne une référence !
    Attention, je le redis, et on te le répétera à l'envie: en informatique, qui ne comprend que le fait que le courent passe à un point donné ou non, chaque mot compte.

    Comme l'a si bien fait remarquer Bousk, tu as un gros problème de vocabulaire:

    Une variable représente n'importe quelle donnée qui est susceptible d'être modifiée. On peut la mettre en opposition avec le terme constante, qui représente une donnée qui ne sera jamais modifiée.

    Une variable peut prendre différentes formes:
    • une valeur : c'est ce que l'on obtiens lorsque l'on écrit un code comme int i = 3; car i est une donnée (qui peut être modifiée) dont la valeur est 3
    • une référence: un alias sur une autre donnée (qui doit exister) que l'on obtient avec un code comme Point & laReference = lePoints; // laReference est un alias de lepoint on donne, dans un contexte particulier, un "autre nom" pour désigner la même donnée, mais tout ce qu'une des donnée subit se répercute sur l'autre nom, et ce, dans les deux sens
    • un pointeur : une donnée numérique entière (généralement non signée) qui représente l'adresse mémoire à laquelle "on est censé" trouver la donnée du type indiqué, que l'on obtient avec un code proche de Point * lePointeur = & lePoint; // lePointeur est un pointeur auquel on donne l'adresse à laquelle se trouve la donnée lePoint

    C'est dans le cadre de ces deux derniers points que l'on constate que l'esperluette représente deux choses totalement différentes en fonction du contexte:

    Dans le deuxième cas, nous sommes dans le contexte d'une déclaration (parce que le nom de la donnée est précédée d'une information concernant le type de donnée qu'elle représente), ce qui fait que laReference est ... une référence sur une donnée de type Point

    Dans le troisième cas, nous sommes dans le contexte d'un accès, vu que l'on veut assigner l'adresse mémoire à laquelle se trouve la donnée lePoint à une donnée de type "pointeur sur Point" (parce que le nom de la donnée n'est pas précédée du type de donnée qu'elle représente).

    C'est donc l'opérateur "address of" (adresse de), qui permet ... de récupérer l'adresse mémoire à laquelle nous trouverons la donnée lePoint à l'exclusion de toute autre (du moins, aussi longtemps que la donnée lePoint existera).

    Ces trois cas représentent autant de possibilités de définir ce que l'on appelle de manière générale une "variable". Les mêmes cas pourraient être utilisés avec le mot clé const (ou même le mot clé constexpr) pour désigner des constantes en cas de besoin.

    Cependant, l'usage que l'on pourra faire de ces trois données sera différent en fonction du cas choisi parce qu'ils nous permettent de déclarer une donnée comme étant "une valeur", "une référence" ou "un pointeur".

    Ensuite, on peut faire la distinction entre "variable" et "paramètre" (ou même "argument"):
    • Une variable est une donnée que l'on déclare pour "notre usage personnel". Chaque fois que tu as besoin de manipuler une donnée "qui vient de nulle part" au sein d'une fonction, tu déclare une variable (ou une constante)
    • Un argument désigne la donnée que tu dois impérativement transmettre à la "boite noire" représentée par une fonction (cf l'autre discussion ) pour qu'elle soit en mesure de faire le taf que l'on attend de sa part, du point de vue de l'utilisateur de la fonction.
    • Un paramètre désigne la donnée qui a été transmise à la "boite noire" représentée par une fonction pour qu'elle soit en mesure de faire le taf que l'on attends de sa part, du point de vue du développeur de la fonction (du point de vue de la personne qui écrit le code de la fonction, uniquement le temps qu'il le fasse et qu'il puisse s'assurer que la fonction fait ce que l'on en attend)

    Les termes "paramètre" et "argument" vont tous les deux avoir pour résultat, au niveau de la fonction qui est appelée, de créer "une variable" dont le type correspond au type de la donnée transmises / à transmettre et dont le nom sera celui du paramètre / de l'argument considéré.

    La seule chose, c'est que la fonction appelée n'est pas capable de définir cette donnée "par elle-même" -- et qui pourrait d'ailleurs changer d'un appel à l'autre -- et que l'on doit donc demander à l'utilisateur de la fonction de nous la transmettre, entre autre, pour garantir que les valeurs représentées par cette donnée correspondent aux valeurs "susceptibles d'intéresser l'utilisateur".
    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

+ Répondre à la discussion
Cette discussion est résolue.
Page 2 sur 2 PremièrePremière 12

Discussions similaires

  1. Réponses: 2
    Dernier message: 02/02/2020, 18h22
  2. Réponses: 16
    Dernier message: 19/02/2010, 09h40
  3. Demande d'aide pour débutant.
    Par pixee dans le forum jQuery
    Réponses: 6
    Dernier message: 31/07/2009, 10h13
  4. [PHP 5.3] Demande aide pour un débutant
    Par xenon1405 dans le forum Langage
    Réponses: 3
    Dernier message: 03/06/2009, 14h48
  5. demande d'aide pour débutant
    Par libremax dans le forum Langage
    Réponses: 6
    Dernier message: 19/11/2008, 19h03

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