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

Langage C++ Discussion :

Besoin d'éclaircissement : this et const (questions simples)


Sujet :

Langage C++

  1. #1
    Membre habitué
    Homme Profil pro
    Doctorant en Astrophysique
    Inscrit en
    Mars 2009
    Messages
    312
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Astrophysique
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2009
    Messages : 312
    Points : 176
    Points
    176
    Par défaut Besoin d'éclaircissement : this et const (questions simples)
    Bonjour.

    Il y a quelque temps, je codais encore "simplement" en C++ et je n'avais pas de problème particulier relatif à ce langage. Mais récemment j'ai eu besoin de surcharger des opérateurs et depuis que j'ai appris à faire cela, un ensemble de choses est devenu beaucoup moins clair .

    Prenons un exemple simple : une classe Vector2 avec 2 variables membres double m_x et double m_y qui fait des opérations sur des vecteurs du plan. L'ensemble des opérateurs ont été surchargés de façon à faire simplement des opérations sur des vecteurs.


    Question 1 : const et &
    Prenons un exemple simple : une fonction addelem(unsigned int n,double a) de la classe Vector2 qui ajoute a à m_x si n=1 et a à m_y si n=2. Quelle est la différence entre intituler la fonction addelem(unsigned int n,double a) et addelem(const unsigned int &n, const double &a) ? Quelle est la "meilleure" écriture ? (j'aurai tendance à dire que la seconde à la meilleure car elle évite de recopier n et a en mémoire mais je ne suis pas du tout sûr de moi).


    Question 2 : fonctions constantes
    J'ai lu qu'il était souvent préférable de placer le mot const après toutes les fonctions get d'une classe (l'argument avancé est la protection des variables membres). Est-ce qu'il est conseillé d'ajouter const après toutes les fonctions d'une classe qui ne touchent pas aux variables membres ? Si oui/non, pourquoi ? La seule fonction du const est-il de protéger les variables membres ?


    Question 3 : utilisation de this
    En gardant l'exemple de la fonction addelem(unsigned int n,double a), j'ai à première vue 2 choix : soit cette fonction renvoie un nouveau Vector2 résultat de l'opération sans modifier le vecteur original, soit elle est de type void et elle modifie m_x ou m_y. Dans le premier cas, si je déclare un Vector2 v dans le programme principal, je pourrai écrire v=v.addelem(1,3.6) si je veux modifier v alors que dans le second cas, je pourrai écrire seulement v.addelem(1,3.6). L'inconvénient du second cas est que cela m'empêche de faire des opérations en même temps avec le nouveau vecteur puisque la fonction est de type void.

    Est-ce que ce troisième code permet "d'allier" les 2 cas précédents :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    Vector2 Vector2::addelem(unsigned int n,double a)
    {
         switch(n)
         {
               case 1 : m_x+=a; break;
               case 2 : m_y+=a; break;
         }
         return *this;
    }
    en me permettant à la fois d'écrire v.addelem(1,3.6) si j'ai juste besoin de modifier le vecteur, mais aussi de faire des opérations avec le vecteur modifié ? Quelle est "dans la pratique" ce qui est le plus couramment utilisé dans du vrai beau code C++ ?


    Merci beaucoup à ceux qui m'aideront à y voir plus clair

  2. #2
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut De l'importance d'être constant.
    Salut,
    Citation Envoyé par Kaluza Voir le message
    Question 1 : const et &
    Prenons un exemple simple : une fonction addelem(unsigned int n,double a) de la classe Vector2 qui ajoute a à m_x si n=1 et a à m_y si n=2. Quelle est la différence entre intituler la fonction addelem(unsigned int n,double a) et addelem(const unsigned int &n, const double &a) ? Quelle est la "meilleure" écriture ? (j'aurai tendance à dire que la seconde à la meilleure car elle évite de recopier n et a en mémoire mais je ne suis pas du tout sûr de moi).
    La réponse n'est pas immédiate et aussi simple que oui ou non. D'abord comprendre les 2 écritures :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    void fonction(type parametre_); // (1)
    void fonction(type const & parametre_); // (2)
    (1) : lors de l'appel de la fonction, le paramètre est copié et c'est cette copie qui est transmise à la fonction.
    (2) : lors de l'appel de la fonction, le paramètre n'est pas copié, c'est une référence qui est transmise à la fonction. Un peu comme une adresse si ça avait été un pointeur. Comme la fonction va manipuler via la référence le même paramètre que la fonction appelant, le const permet d'indiquer que la fonction ne va pas le modifier.
    A priori, on pourrait penser que (2) est préférable à (1) puisqu'on ne fait pas une copie du paramètre. C'est vrai dans la plus part des cas ... sauf en général pour les types primitifs (char, short, int, float, double,...), les itérateurs et les foncteurs. En général, les types primitifs 'coûtent' au mieux - chers, au pire pareil. cf cette discussion et ce message.
    Citation Envoyé par Kaluza Voir le message
    Question 2 : fonctions constantes
    J'ai lu qu'il était souvent préférable de placer le mot const après toutes les fonctions get d'une classe (l'argument avancé est la protection des variables membres). Est-ce qu'il est conseillé d'ajouter const après toutes les fonctions d'une classe qui ne touchent pas aux variables membres ? Si oui/non, pourquoi ? La seule fonction du const est-il de protéger les variables membres ?
    const indique que la fonction ne va pas modifier l'état interne de l'objet. Il est important de le préciser si c'est le cas car cela fait parti du 'contrat' de la classe. D'une part, le compilateur va râler si tu voulais ne pas modifier l'objet mais qu'incidemment tu le modifies. Mais d'autre part, tu renforce la sémantique de cette fonction. Et puis c'est un élément de documentation très fort pour l'utilisation de cette fonction. cf F.A.Q Pourquoi certaines fonctions membres possèdent le mot clé const après leur nom ? et Qu'est-ce que l'immuabilité ?

    Citation Envoyé par Kaluza Voir le message
    Question 3 : utilisation de this
    En gardant l'exemple de la fonction addelem(unsigned int n,double a), j'ai à première vue 2 choix : soit cette fonction renvoie un nouveau Vector2 résultat de l'opération sans modifier le vecteur original, soit elle est de type void et elle modifie m_x ou m_y. Dans le premier cas, si je déclare un Vector2 v dans le programme principal, je pourrai écrire v=v.addelem(1,3.6) si je veux modifier v alors que dans le second cas, je pourrai écrire seulement v.addelem(1,3.6). L'inconvénient du second cas est que cela m'empêche de faire des opérations en même temps avec le nouveau vecteur puisque la fonction est de type void.

    Est-ce que ce troisième code permet "d'allier" les 2 cas précédents :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    Vector2 Vector2::addelem(unsigned int n,double a)
    {
         switch(n)
         {
               case 1 : m_x+=a; break;
               case 2 : m_y+=a; break;
         }
         return *this;
    }
    en me permettant à la fois d'écrire v.addelem(1,3.6) si j'ai juste besoin de modifier le vecteur, mais aussi de faire des opérations avec le vecteur modifié ? Quelle est "dans la pratique" ce qui est le plus couramment utilisé dans du vrai beau code C++ ?
    A nouveau la réponse n'est pas binaire et dépend de ce que tu veux faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    void addelem(unsigned int n,double a); // (1)
    Vector2 addelem(unsigned int n,double a); // (2)
    Vector2& addelem(unsigned int n,double a); // (3)
    (1) ne retourne rien et donc tu ne peux récupérer le résultat comme un nouveau vecteur ou enchaîner les fonctions.
    (2) retourne une copie du résultat. Tu peux donc récupérer cette nouvelle valeur
    (3) souvent utilisé pour les opérations retourne une référence sur le résultat et permet d'enchainer les opérations sur le même objet : v.addelem(1,1.5).addelem(2,2.5);

  3. #3
    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 : 50
    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
    1/ Dans le passage par valeur, à l'intérieur de la fonction, tu pourras changer la valeur de n et a (mais ces changements seront locaux, pas visibles depuis l'extérieur). Pas dans le passage par référence constante.
    Par le passage par valeur, tu passe par le constructeur de copie de n et a. Pas dans l'autre cas (ce qui peut avoir des impacts si les objets sont non copiables, et peut avoir des impacts si les objets sont copiables mais que la copie est coûteuse en temps, ce qui n'est pas le cas avec les types de base).

    2/ Non, ça a deux effets : Tout d'abord, ça permet de s'assurer que la fonction ne modifie pas de données membre, mais en plus, ça permet d'appeler la fonction sur un objet constant. Par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    void f(A const &a)
    {
      a.fct();
    }
    Si fct n'est pas const, le code ne compilera pas.

    3/ Les troisième code tel que tu le mets est certainement une très mauvaise pratique
    En effet, tu modifie l'objet, et tu en renvoies une copie. Ce qu'on trouve généralement c'est :
    - Du code qui va ne pas modifier l'objet (et d'ailleurs, on trouve souvent des objets entièrement non modifiables)
    - Du code qui va modifier l'objet, et retourner une référence sur celui-ci, permettant ainsi le chaînage d'appels.

  4. #4
    Membre habitué
    Homme Profil pro
    Doctorant en Astrophysique
    Inscrit en
    Mars 2009
    Messages
    312
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Astrophysique
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2009
    Messages : 312
    Points : 176
    Points
    176
    Par défaut
    Merci pour vos réponses.
    Donc si on résume pour voir si j'ai bien compris :


    1) En gros quand les paramètres que l'on passe à la fonction sont des types primitifs, j'utilise plutôt la forme SANS les const et &, alors que lorsque ce sont des types non primitifs (genre des classes ou des structures), la forme AVEC les const et & trouve sa place (si il n'y a pas besoin de modifier ces éléments).


    2) Je mettrai désormais des const après chaque fonction d'une classe qui n'est pas censé modifier les variables membres.

    3) Lorsque vous dites :
    3DArchi : (3) souvent utilisé pour les opérations retourne une référence sur le résultat et permet d'enchainer les opérations sur le même objet : v.addelem(1,1.5).addelem(2,2.5);
    JolyLoic : Du code qui va modifier l'objet, et retourner une référence sur celui-ci, permettant ainsi le chaînage d'appels.

    Vous faites référence à la même forme n'est-ce pas ?
    Dans ce cas est-ce que la fonction s'écrirait :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    Vector2& Vector2::addelem(unsigned int n,double a)
    {
         switch(n)
         {
               case 1 : m_x+=a; break;
               case 2 : m_y+=a; break;
         }
         return *this;
    }
    (juste le & de différent par rapport à la dernière écriture). Parce que c'est bien ce que je veux faire : modifier l'objet et renvoyer une référence pour pouvoir enchainer les opérations dessus.

    Dans le cas où j'implémente le code ci-dessus, on est d'accord qu'écrire v=v.addelem(n,a) (même si c'est un peu débile du coup) est strictement équivalent à v.addelem(n,a) ?

    Merci beaucoup

  5. #5
    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 : 50
    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 Kaluza Voir le message
    3) Lorsque vous dites :
    3DArchi : (3) souvent utilisé pour les opérations retourne une référence sur le résultat et permet d'enchainer les opérations sur le même objet : v.addelem(1,1.5).addelem(2,2.5);
    JolyLoic : Du code qui va modifier l'objet, et retourner une référence sur celui-ci, permettant ainsi le chaînage d'appels.

    Vous faites référence à la même forme n'est-ce pas ?
    Oui
    Citation Envoyé par Kaluza Voir le message
    Dans le cas où j'implémente le code ci-dessus, on est d'accord qu'écrire v=v.addelem(n,a) (même si c'est un peu débile du coup) est strictement équivalent à v.addelem(n,a) ?
    Pas strictement. dans le second cas, tu vas avoir en plus un appel à ton opérateur= pour affecter à v la valeur qu'il a déjà. Ce qui peut paradoxalement causer des surprises, selon comment tu as défini ton opérateur=.

Discussions similaires

  1. Réponses: 7
    Dernier message: 28/09/2004, 14h27
  2. Question simple sur la libération des objets
    Par gibet_b dans le forum Langage
    Réponses: 2
    Dernier message: 12/07/2004, 10h01
  3. DTS : Question simple sur sources de données
    Par guignol dans le forum MS SQL Server
    Réponses: 2
    Dernier message: 28/05/2004, 12h09
  4. [LG]Choix du pascal ou autre ? Questions simples...
    Par vlacq dans le forum Langage
    Réponses: 5
    Dernier message: 30/01/2004, 23h42
  5. Question simple sur les threads :)
    Par momox dans le forum C++Builder
    Réponses: 2
    Dernier message: 15/06/2003, 04h13

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