Est-ce une bonne idée de «# me définir (* ceci)»?

19

Cette macro peut être définie dans un en-tête global, ou mieux, en tant que paramètre de ligne de commande du compilateur:

#define me (*this)

Et un exemple d'utilisation:

some_header.h:

inline void Update()
{
    /* ... */
}

main.cpp:

#include "some_header.h"

class A {
public:
    void SetX(int x)
    {
        me.x = x;   
        me.Update(); 
    }

    void SomeOtherFunction()
    {
        ::Update();
    }

    /*
        100 or more lines
        ... 
    */

    void Update()
    {
        // ... 
    }

    int x;  
};

Ainsi, dans une méthode de classe lorsque j'accède à un membre de la classe, j'utilise toujours meet lorsque j'accède à un identifiant global, j'utilise toujours ::. Cela donne au lecteur qui n'est pas familier avec le code (probablement moi-même après quelques mois) des informations localisées de ce qui est accessible sans avoir besoin de chercher ailleurs. Je veux définir mecar je trouve l'utilisation this->partout trop bruyante et laide. Mais peut- #define me (*this)on considérer comme une bonne pratique C ++? Y a-t-il des points pratiques problématiques avec la memacro? Et si vous, en tant que programmeur C ++, serez le lecteur de code à l'aide de la memacro, cela vous plaira ou non?

Edit: Parce que beaucoup de gens ne soutiennent pas spécifiquement contre l'utilisation me, mais contre expliquent généralement cela. Je pense qu'il n'est peut-être pas clair quels sont les avantages de «l'expliciter partout».

Quels sont les avantages de "l'expliciter partout"?

  • En tant que lecteur du code, vous avez la certitude de ce qui est accédé et vous pouvez vous concentrer sur des choses différentes que de vérifier - dans un code distant - que vous avez réellement accès à ce que vous pensez être accédé.
  • Vous pouvez utiliser la fonction de recherche plus spécifiquement. La recherche " this->x" peut vous donner plus de résultats que la recherche " x"
  • Lorsque vous supprimez ou renommez un membre, le compilateur vous avertit de manière fiable aux endroits où ce membre est utilisé. (Certaines fonctions globales peuvent avoir le même nom et exister, vous pouvez introduire une erreur si vous n'utilisez pas cela explicitement).
  • Lorsque vous refactorisez du code et rendez explicite la fonction non membre à partir d'un membre (pour une meilleure encapsulation), cela vous montre où vous devez modifier et vous pouvez facilement le remplacer par un pointeur sur l'instance de classe donnée comme paramètre de fonction non membre
  • Généralement, lorsque vous modifiez du code, il y a plus de possibilités d'erreurs lorsque vous n'utilisez pas cela explicitement que lorsque vous utilisez cela explicitement partout.
  • Explicite, c'est moins bruyant que «m_» explicite lorsque vous accédez à un membre de l'extérieur ( object.membervs object.m_member) (merci à @Kaz de repérer ce point)
  • Explicitement, cela résout le problème universellement pour tous les membres - attributs et méthodes, alors que «m_» ou un autre préfixe n'est pratiquement utilisable que pour les attributs.

Je voudrais peaufiner et étendre cette liste, dites-moi si vous connaissez d'autres avantages et utilisez des cas pour l'expliquer partout .

user3123061
la source
3
Vous devez éviter d'avoir des arguments de fonction et des membres avec le même nom, ainsi que d'éviter explicitement "ceci".
pjc50
4
Me rappelle le code VB de mon patron. Moi Moi Moi partout. Cela semble égoïste. : p Quoi qu'il en soit, les réponses jusqu'à présent disent tout.
MetalMikester
19
C'est une merveilleuse idée! Et pour ceux qui viennent d'un background Python, pourquoi pas #define self (*this)? Vous pouvez même mélanger les deux macros et avoir certains fichiers imitant VB et d'autres Python. :)
logc
11
Pourquoi ne pas aller juste tous dehors et faire: #include "vb.h", #Include pascal.hou #include FOTRAN.het ont la prochaine personne à toucher votre code soumettre à TDWTF .
Dan Neely
4
Oh s'il te plait, non. Vous pourriez vous éviter quelques ennuis en déclarant simplement les attributs comme me_x.
Gort the Robot

Réponses:

90

Non, ça ne l'est pas.

Attention au programmeur qui maintiendra votre code plusieurs années à partir de maintenant longtemps après votre départ pour des pâturages plus verts et suivra les conventions courantes du langage que vous utilisez. En C ++, vous n'avez presque jamais à écrire this, car la classe est incluse dans l'ordre de résolution des symboles et lorsqu'un symbole est trouvé dans la portée de la classe, cela this->est implicite. Donc, ne l'écrivez pas comme tout le monde.

Si vous confondez souvent les symboles provenant de la portée de la classe, l'approche habituelle consiste à utiliser un modèle de dénomination commun pour les membres (champs et méthodes parfois privées; je ne l'ai pas vu utilisé pour les méthodes publiques). Les plus courants incluent le suffixe avec _ou le préfixe avec m_ou m.

Jan Hudec
la source
12
"En C ++, vous n'avez presque jamais à écrire ceci" c'est une convention totalement indépendante de la question. Certaines personnes (comme moi) préfèrent écrire thispour indiquer clairement que la variable n'est pas locale à la méthode. La question ici est de savoir si la macro medoit exister, et non si thisquelque chose doit être utilisé.
Mehrdad
8
@Mehrdad: Parce que l'OP dit explicitement qu'il utilise à la me.place de this->. Comme il n'y a pas besoin de, this->il n'y a pas besoin me.et donc pas besoin de la macro.
Martin York
11
@LokiAstari: Rien dans la question n'indique même à distance que l'OP se demande si this->"est nécessaire". En fait, le PO dit qu'il utilise this->malgré le fait que ce ne soit pas nécessaire. La question est de savoir si elle me.doit être utilisée à la place de this-> , à laquelle cette réponse ne répond pas réellement.
Mehrdad
4
@Mehrdad: Mais seulement mettre oui ou non fait une réponse très ennuyeuse. Ainsi, les explications sont généralement très utiles pour expliquer comment la réponse est obtenue (est encouragé par Comment répondre ). La conclusion n'est pas à utiliser parce que le PO a la mauvaise attitude au départ à propos de l'utilisation thiset donc une discussion sur ce point est nécessaire pour parvenir à une conclusion pleinement équilibrée.
Martin York
1
+1 pour avoir expliqué pourquoi, au lieu de simplement dire "non, c'est une mauvaise pratique".
user253751
42

Donc, vous voulez créer une nouvelle langue. Faites-le ensuite et ne paralysez pas C ++.

Il y a plusieurs raisons de ne pas le faire:

  1. Chaque norme de codage normale suggérera d'éviter les macros ( voici pourquoi )
  2. Il est plus difficile de maintenir le code avec de telles macros. Tout le monde qui programme en C ++ sait ce que thisc'est, et en ajoutant une telle macro, vous ajoutez en fait un nouveau mot-clé. Et si chacun présente quelque chose qu'il aime? À quoi ressemblerait le code?
  3. Vous ne devriez pas utiliser (*this).ou pas this->du tout, sauf dans certains cas spéciaux (voir cette réponse et recherchez "this->")

Votre code n'est pas différent de celui #define R returnque j'ai vu dans le code réel. Raison? Moins de frappe!


Aller légèrement hors sujet, mais ici je vais développer le point 3 (ne pas utiliser (*this).ou this->en cours).

Tout d'abord, (*this).ou this->sont utilisés pour accéder aux variables membres ou aux fonctions de l'objet. Son utilisation n'a aucun sens et signifie plus de frappe. De plus, la lecture d'un tel code est plus difficile, car il y a plus de texte. Cela signifie un entretien plus difficile.

Alors, quels sont les cas où vous devez utiliser this->?

(a) Choix malheureux du nom de l'argument.

Dans cet exemple, this->est obligatoire, car l'argument a le même nom que la variable membre:

struct A {
  int v;
  void foo( int v ) {
    this->v =v;
  }
};

(b) Lorsqu'il s'agit de modèles et d'héritage (voir ceci )

Cet exemple ne parviendra pas à compiler, car le compilateur ne sait pas à quelle variable nommée vaccéder.

template< typename T >
struct A {
  A(const T& vValue):v(vValue){}

  T v;
};

template< typename T >
struct B : A<T>
{
    B(const T& vValue):A<T>(vValue){}

    void foo( const T & newV ) {
      v = newV;
    }
};
BЈовић
la source
1
"Donc, vous voulez créer un nouveau langage. Alors faites-le, et ne paralyse pas le c ++." - Je ne suis pas d'accord. Une force majeure du C et du C ++ est leur flexibilité. L'OP n'entrave pas le C ++, il utilise simplement sa flexibilité d'une manière particulière pour le rendre plus facile à coder.
user253751
2
@immibis C'est vrai. Ce qui est plus facile pour lui, est plus difficile pour tout le monde. Lorsque vous travaillez en équipe, mettre de telles bêtises dans le code ne vous rendra pas très favorable.
BЈовић
2
Votre réponse n'est pas différente de "définit sont mauvais".
M. Lister
7
@MrLister Ma réponse est "les définitions stupides sont mauvaises". La macro dans la question est stupide à utiliser, et comme je l'ai montré, pas du tout nécessaire. Le code est bon et encore meilleur sans *this.etthis->
BЈовић
1
@Mehrdad Les gens ajoutent-ils toujours des préfixes et des suffixes aux variables membres? Je pensais que c'était "corrigé" avec des livres comme Clean Code . this->est aussi utile qu'en autopré-c ++ 11. En d'autres termes, cela augmente simplement le bruit du code.
BЈовић
26

Je suggère de ne pas faire ça. Cela donne à un lecteur qui n'est pas familier avec votre macro un gros " WTF " chaque fois qu'il le voit. Le code n'est pas plus lisible lorsque l'on invente de "nouvelles conventions" par rapport à celles généralement acceptées sans aucun besoin réel.

utiliser ça-> partout est trop bruyant et moche

Cela peut vous sembler vrai, peut-être parce que vous avez fait beaucoup de programmation dans les langages en utilisant le mot-clé me(Visual Basic, je suppose?). Mais en fait, il s'agit simplement de s'y habituer - this->c'est assez court, et je pense que la plupart des programmeurs C ++ expérimentés seront en désaccord avec votre opinion. Et dans le cas ci-dessus, ni l'utilisation this->ni l'utilisation de men'est appropriée - vous obtenez le plus petit encombrement en omettant ces mots clés lors de l'accès aux membres de données à l'intérieur des fonctions membres.

Si vous voulez que vos variables membres privées soient distinguées des variables locales, ajoutez quelque chose de lien m_comme préfixe, ou un trait de soulignement comme suffixe (mais comme vous pouvez le voir ici , même cette convention est "trop ​​bruyante" pour beaucoup de gens).

Doc Brown
la source
12
_devrait être suffixé ; en tant que préfixe, il est réservé aux symboles internes des bibliothèques standard et des extensions du fournisseur.
Jan Hudec
4
@JanHudec Uniquement s'il commence par __(deux traits de soulignement) ou un trait de soulignement suivi d'une lettre majuscule. Un simple trait de soulignement suivi d'une lettre minuscule convient, tant qu'il n'est pas dans l'espace de noms global.
Eric Finn
1
@EricFinn: J'ai combiné les deux règles en une seule. Deux traits de soulignement ou trait de soulignement suivis d'une lettre majuscule sont pour les symboles internes et un trait de soulignement simple suivi d'une lettre minuscule est pour les extensions spécifiques au système. Les applications ne doivent pas non plus utiliser.
Jan Hudec
@JanHudec n'était pas au courant de la règle pour les extensions spécifiques au système. Je vais toutefois laisser mon commentaire précédent pour fournir un certain contexte à votre commentaire.
Eric Finn
2
@JanHudec: Les minuscules correspondent aux extensions spécifiques au système? Pourriez-vous publier une référence à la partie de la norme C ++ qui indique cela?
Mehrdad
18

Veuillez ne pas le faire! J'essaie de faire face à une grande base de code où les macros sont partout pour enregistrer la frappe. La mauvaise chose à redéfinir cela pour moi est que le préprocesseur le remplacera partout même lorsque cela n'est pas dans la portée / ne s'applique pas, par exemple une fonction autonome, votre collègue peut avoir une variable locale qui m'appelle ailleurs. . (s) il ne sera pas content de déboguer ... Vous finissez par avoir des macros que vous ne pouvez pas utiliser dans toutes les étendues.

truschival
la source
6

NON!

Imaginez la confusion qui se produira si quelqu'un # inclut cet en-tête, ne connaissant pas votre astuce, et ailleurs dans son fichier, il a une variable ou une fonction appelée "moi". Ils seraient horriblement confus par tout message d'erreur impénétrable qui serait imprimé.

Larry Gritz
la source
En pratique, il est fort probable qu'ils survolent «moi» avec leur IDE et voient la définition.
user253751
2
@immibis: En pratique, cependant, ils sont également susceptibles de dire quelque chose comme "qui a écrit cette merde?". : P
cHao
@immibis: la plupart des meilleurs codeurs avec lesquels je travaille n'utilisent pas la souris - c'est trop lent.
JBRWilkinson
En effet: enfoncer cela dans un en-tête est le vrai problème. Si vous l'utilisez dans votre fichier cpp, je ne vois aucun problème. Si nous avions standard pragma push, je conseillerais comment le faire dans un en-tête, mais nous ne le faisons pas. Répète après moi. #defines sont globaux.
Joshua