web-dev-qa-db-fra.com

Pourquoi utiliser les préfixes sur les variables membres dans les classes C++

Beaucoup de code C++ utilise des conventions syntaxiques pour marquer les variables de membre. Les exemples courants incluent

  • m _ memberName pour les membres du public (les membres du public sont utilisés)
  • _ memberName pour les membres privés ou tous les membres

D'autres essaient d'appliquer en utilisant ceci -> membre chaque fois qu'une variable membre est utilisée.

D'après mon expérience, la plupart des bases de code plus grandes ne parviennent pas à appliquer ces règles de manière cohérente.

Dans d’autres langues, ces conventions sont beaucoup moins répandues. Je ne le vois que de temps en temps en code Java ou C #. Je pense ne l'avoir jamais vu en code Ruby ou Python. Ainsi, il semble exister une tendance des langues plus modernes à ne pas utiliser de balisage spécial pour les variables membres.

Cette convention est-elle encore utile aujourd'hui en C++ ou s'agit-il simplement d'un anachronisme? D'autant plus qu'il est utilisé de manière si incohérente dans toutes les bibliothèques. Les autres langues indiquées ne permettent-elles pas de se passer des préfixes de membre?

134
VoidPointer

Vous devez être prudent avec l'utilisation d'un trait de soulignement principal. Un tiret bas avant une lettre majuscule dans un mot est réservé ..__ Par exemple:

_Foo

_L

sont tous des mots réservés alors que

_foo

_l

ne sont pas. Il existe d'autres situations dans lesquelles les traits de soulignement avant les lettres minuscules ne sont pas autorisés. Dans mon cas spécifique, j’ai trouvé que le _L était réservé par Visual C++ 2005 et que le conflit a généré des résultats inattendus.

Je suis sur le point de savoir combien il est utile de marquer les variables locales.

Voici un lien sur lequel les identificateurs sont réservés: Quelles sont les règles d'utilisation d'un trait de soulignement dans un identifiant C++?

45
Juan

Je suis tout en faveur de préfixes bien faits.

Je pense que la notation hongroise (système) est responsable de la plupart des erreurs de frappe que les préfixes ont.

Cette notation est largement inutile dans les langages fortement typés, par exemple. en C++ "lpsz" pour vous dire que votre chaîne est un long pointeur sur une chaîne terminée par zéro, quand: l'architecture segmentée est une histoire ancienne, les chaînes C++ sont par convention des pointeurs classiques sur des tableaux de caractères terminés par zéro, et ce n'est pas si difficile savoir que "customerName" est une chaîne!

Cependant, j'utilise des préfixes pour spécifier l'utilisation d'une variable (essentiellement "Apps Hungarian", bien que je préfère éviter le terme hongrois, car une association mauvaise et injuste avec System Hungarian), et il s’agit d’une approche très pratique gain de temps et réduisant les bugs.

J'utilise:

  • m pour les membres
  • c pour les constantes/readonlys
  • p pour pointeur (et pp pour pointeur à pointeur)
  • v pour volatile
  • s pour statique
  • i pour les index et les itérateurs
  • e pour les événements

Lorsque je souhaite préciser le type , j'utilise des suffixes standard (par exemple, List, ComboBox, etc.).

Cela fait prendre conscience au programmeur de l'utilisation de la variable chaque fois qu'il la voit/l'utilise. Le cas le plus important est sans doute le "p" du pointeur (car l'utilisation change de var. À var-> et vous devez être beaucoup plus prudent avec les pointeurs - NULL, arithmétique de pointeur, etc.), mais tous les autres sont très pratiques.

Par exemple, vous pouvez utiliser le même nom de variable de plusieurs manières dans une même fonction: (ici un exemple C++, mais cela s'applique également à de nombreux langages)

MyClass::MyClass(int numItems)
{
    mNumItems = numItems;
    for (int iItem = 0; iItem < mNumItems; iItem++)
    {
        Item *pItem = new Item();
        itemList[iItem] = pItem;
    }
}

Vous pouvez voir ici:

  • Pas de confusion entre membre et paramètre
  • Pas de confusion entre index/iterator et items
  • Utilisation d'un ensemble de variables clairement liées (liste d'éléments, pointeur et index) qui évitent les nombreux pièges des noms génériques (vagues) tels que "count", "index".
  • Les préfixes réduisent la saisie (plus courts et fonctionnent mieux avec la complétion automatique) que des variantes telles que "itemIndex" et "itemPtr"

Un autre avantage des itérateurs "iName" est que je n'indexe jamais un tableau avec le mauvais index et que, si je copie une boucle dans une autre boucle, je n'ai pas à refactoriser l'une des variables d'index de la boucle.

Comparez cet exemple peu réaliste:

for (int i = 0; i < 100; i++)
    for (int j = 0; j < 5; j++)
        list[i].score += other[j].score;

(ce qui est difficile à lire et conduit souvent à l'utilisation de "i" où "j" était destiné)

avec:

for (int iCompany = 0; iCompany < numCompanies; iCompany++)
    for (int iUser = 0; iUser < numUsers; iUser++)
       companyList[iCompany].score += userList[iUser].score;

(ce qui est beaucoup plus lisible et supprime toute confusion en matière d'indexation. Avec la saisie automatique dans les IDE modernes, il est également rapide et facile à saisir)

L'avantage suivant est que les extraits de code ne nécessitent pas de contexte pour être compris. Je peux copier deux lignes de code dans un courrier électronique ou un document. Quiconque lit cet extrait de code peut faire la différence entre tous les membres, constantes, pointeurs, index, etc. Je n'ai pas à ajouter "oh, et faites attention car "data" est un pointeur sur un pointeur ", parce qu'il s'appelle" ppData ".

Et pour la même raison, je n'ai pas besoin de bouger les yeux d'une ligne de code pour le comprendre. Je n'ai pas besoin de chercher dans le code pour savoir si "données" est une locale, un paramètre, un membre ou une constante. Je n'ai pas besoin de déplacer ma main vers la souris pour pouvoir passer le pointeur sur "données", puis attendre qu'une info-bulle (qui n'apparaisse jamais) apparaisse. Ainsi, les programmeurs peuvent lire et comprendre le code beaucoup plus rapidement , car ils ne perdent pas de temps à chercher en avant ou en arrière.

(Si vous ne perdez pas votre temps à chercher du travail, trouvez du code que vous avez écrit il y a un an et que vous n'avez pas regardé depuis. Ouvrez le fichier et passez à autre chose Voyez à quelle distance vous pouvez lire à partir de ce point avant de savoir si un élément est un membre, un paramètre ou une locale, passez maintenant à un autre emplacement aléatoire ... C’est ce que nous faisons tous à longueur de journée. lorsque nous sommes en train de parcourir le code de quelqu'un d'autre ou d'essayer de comprendre comment appeler sa fonction)

Le préfixe "m" évite également la notation ("IM-") laide et verbeuse (IMHO), et l'incohérence qu'il garantit (même si vous faites attention, vous obtiendrez généralement un mélange de "ceci-> données" et 'données' dans la même classe, car rien n'impose une orthographe cohérente du nom).

La notation 'this' est destinée à résoudre l'ambiguïté - mais pourquoi quelqu'un voudrait-il délibérément écrire du code qui peut être ambigu? L'ambiguïté provoquera un bogue tôt ou tard. Et dans certaines langues, "this" ne peut pas être utilisé pour les membres statiques, vous devez donc introduire des "cas spéciaux" dans votre style de codage. Je préfère avoir une seule règle de codage simple qui s'applique partout - explicite, sans ambiguïté et cohérente.

Le dernier avantage majeur concerne Intellisense et l'auto-complétion. Essayez d’utiliser Intellisense sur un Windows Form pour rechercher un événement - vous devez faire défiler des centaines de méthodes de classe de base mystérieuses que vous n’aurez jamais besoin d’appeler pour rechercher les événements. Mais si chaque événement avait un préfixe "e", il serait automatiquement répertorié dans un groupe sous "e". Ainsi, la préfixe fonctionne pour regrouper les membres, consts, événements, etc. dans la liste intellisense, ce qui rend beaucoup plus rapide et plus facile la recherche des noms que vous souhaitez. (Habituellement, une méthode peut avoir environ 20 à 50 valeurs (locales, paramètres, membres, consts, événements) accessibles dans sa portée. Mais après avoir tapé le préfixe (je veux utiliser un index maintenant, je tape donc 'i. .. '), on ne me présente que 2 à 5 options de saisie semi-automatique. L'attribut supplémentaire utilisé par les utilisateurs pour les préfixes et les noms significatifs réduit considérablement l'espace de recherche et accélère considérablement la vitesse de développement)

Je suis un programmeur paresseux et la convention ci-dessus me fait économiser beaucoup de travail. Je peux coder plus rapidement et commettre beaucoup moins d’erreurs car je sais comment utiliser chaque variable.


Arguments contre

Alors, quels sont les inconvénients? Les arguments typiques contre les préfixes sont:

  • "Les schémas de préfixes sont mauvais/mauvais" . Je conviens que "m_lpsz" et ses semblables sont mal pensés et totalement inutiles. C'est pourquoi je vous conseillerais d'utiliser une notation bien conçue, conçue pour répondre à vos besoins, plutôt que de copier quelque chose qui ne convient pas à votre contexte. (Utilisez le bon outil pour le travail).

  • "Si je change l'usage de quelque chose, je dois le renommer" . Oui, bien sûr, c’est ce qu’est la refactorisation, et c’est pourquoi les IDE disposent d’outils de refactorisation pour faire ce travail rapidement et sans douleur. Même sans préfixes, changer l'utilisation d'une variable signifie presque certainement que son nom doit être changé .

  • "Les préfixes me confondent" . Comme tous les outils jusqu'à ce que vous appreniez à vous en servir. Une fois que votre cerveau s'est habitué aux modèles de dénomination, il filtrera automatiquement les informations et vous ne craindrez plus que les préfixes soient là. Mais vous devez utiliser un schéma comme celui-ci pendant une semaine ou deux avant de devenir vraiment "fluide". Et c'est à ce moment-là que beaucoup de gens regardent l'ancien code et commencent à se demander comment ils ont pu gérer sans un bon schéma de préfixes.

  • "Je peux simplement regarder le code pour résoudre ce problème" . Oui, mais vous n'avez pas besoin de perdre du temps à chercher ailleurs dans le code ni à vous souvenir de chaque détail lorsque le problème est réglé sur le fond, sur lequel votre œil est déjà concentré.

  • (Une partie de) cette information peut être trouvée simplement en attendant qu'une info-bulle apparaisse sur ma variable . Oui. Là où cela est supporté, pour certains types de préfixes, lorsque votre code est compilé proprement, après une attente, vous pouvez lire une description et trouver les informations que le préfixe aurait transmises instantanément. Je pense que le préfixe est une approche plus simple, plus fiable et plus efficace.

  • "C'est plus en tapant" . Vraiment? Un personnage entier de plus? Ou bien est-ce - avec IDE outils d'auto-complétion, cela réduira souvent la saisie, car chaque préfixe réduit considérablement l'espace de recherche. Appuyez sur "e" et les trois événements de votre classe apparaissent dans intellisense. Appuyez sur "c" et les cinq constantes sont répertoriées.

  • "Je peux utiliser this-> au lieu de m" . Eh bien oui, vous pouvez. Mais ce n'est qu'un préfixe beaucoup plus laid et plus verbeux! Seulement, il comporte un risque beaucoup plus grand (en particulier dans les équipes) car il est facultatif pour le compilateur , et son utilisation est donc souvent incohérente. m est en revanche bref, clair, explicite et non facultatif, il est donc beaucoup plus difficile de faire des erreurs en l'utilisant.

222
Jason Williams

Je n'utilise généralement pas de préfixe pour les variables membres.

J'avais l'habitude d'utiliser un préfixe m, jusqu'à ce que quelqu'un indique que "C++ a déjà un préfixe standard pour l'accès membre: this->.

C'est ce que j'utilise maintenant. C'est-à-dire, lorsqu'il y a ambiguïté , j'ajoute le préfixe this->, mais en général, il n'y a pas d'ambiguïté et je peux simplement me référer directement au nom de la variable.

Pour moi, c'est le meilleur des deux mondes. J'ai un préfixe que je peux utiliser quand j'en ai besoin et je suis libre de le laisser autant que possible.

Bien entendu, le contraire évident est "oui, mais vous ne pouvez pas voir d'un coup d'œil si une variable est un membre de la classe ou non".

A quoi je dis "Et alors? Si vous avez besoin de le savoir, votre classe a probablement trop d'état. Ou la fonction est trop grande et compliquée".

En pratique, j'ai trouvé que cela fonctionnait extrêmement bien. En prime, cela me permet de promouvoir facilement une variable locale auprès d'un membre de classe (ou l'inverse), sans avoir à la renommer.

Et le meilleur de tout, c'est cohérent! Je ne dois rien faire de spécial ni me souvenir de conventions pour maintenir la cohérence. 


À propos, vous ne devriez pas utiliser les soulignés principaux pour les membres de votre classe. Vous vous approchez des noms réservés par l'implémentation.

La norme réserve tous les noms commençant par un double souligné ou un souligné, suivi d'une lettre majuscule. Il réserve également tous les noms commençant par un seul trait de soulignement dans l'espace de noms global .

Donc, un membre de la classe avec un tiret bas suivi d'une lettre minuscule est légal, mais tôt ou tard, vous ferez de même pour un identifiant commençant par une majuscule, ou enfreignez l'une des règles ci-dessus.

Il est donc plus facile d’éviter les traits de soulignement. Utilisez un trait de soulignement postfixé ou un préfixe m_ ou simplement m si vous souhaitez coder la portée dans le nom de la variable.

106
jalf

Je préfère les underscores postfix, tels que:

class Foo
{
   private:
      int bar_;

   public:
      int bar() { return bar_; }
};
32
jkeys

Dernièrement, j'ai eu tendance à préférer le préfixe m_ au lieu de ne pas en avoir du tout. Les raisons ne sont pas si importantes qu'il soit important de marquer les variables membres, mais d'éviter les ambiguïtés, disons que vous avez un code comme celui-ci:

void set_foo(int foo) { foo = foo; }

Celui de cause ne fonctionne pas, un seul foo est autorisé. Donc, vos options sont:

  • this->foo = foo;

    Je n'aime pas ça, car cela crée une ombrage des paramètres, vous ne pouvez plus utiliser g++ -Wshadow warnings, il est également plus long de taper puis m_. Vous avez également toujours des conflits de noms entre les variables et les fonctions lorsque vous avez un int foo; et un int foo();.

  • foo = foo_; ou foo = arg_foo;

    Je l'utilise depuis un moment, mais les listes d'arguments sont moches, la documentation n'aurait pas dû traiter de la désambiguïté des noms dans l'implémentation. Les conflits de nommage entre les variables et les fonctions existent également ici.

  • m_foo = foo;

    La documentation de l'API reste propre, vous n'obtenez aucune ambiguïté entre les fonctions membres et les variables, et il est plus court de taper puis this->. Le seul inconvénient est que les structures de POD sont laides, mais comme elles ne souffrent pas de l’ambiguïté du nom, il n’est pas nécessaire de l’utiliser avec elles. Avoir un préfixe unique facilite également quelques opérations de recherche et remplacement.

  • foo_ = foo;

    La plupart des avantages de m_ s'appliquent, mais je le rejette pour des raisons esthétiques. Un trait de soulignement inférieur ou supérieur rend simplement la variable incomplète et déséquilibrée. m_ semble juste mieux. L'utilisation de m_ est également plus extensible, car vous pouvez utiliser g_ pour les globaux et s_ pour la statique.

PS: La raison pour laquelle vous ne voyez pas m_ dans Python ou Ruby est parce que les deux langages imposent leur propre préfixe, Ruby utilise @ pour les variables membres et Python nécessite self..

19
Grumbel

Lors de la lecture d'une fonction membre, il est absolument essentiel de savoir qui "possède" chaque variable pour en comprendre le sens. Dans une fonction comme celle-ci:

void Foo::bar( int apples )
{
    int bananas = apples + grapes;
    melons = grapes * bananas;
    spuds += melons;
}

... il est assez facile de voir d'où viennent les pommes et les bananes, mais qu'en est-il des raisins, des melons et des pommes de terre? Devrions-nous regarder dans l'espace de noms global? Dans la déclaration de classe? La variable est-elle membre de cet objet ou membre de la classe de cet objet? Sans connaître la réponse à ces questions, vous ne pouvez pas comprendre le code. Et dans une fonction plus longue, même les déclarations de variables locales comme les pommes et les bananes peuvent se perdre dans le brassage.

L'ajout préalable d'une étiquette cohérente pour les variables globales, les variables membres et les variables membres statiques (peut-être g_, m_ et s_ respectivement) clarifie instantanément la situation.

void Foo::bar( int apples )
{
    int bananas = apples + g_grapes;
    m_melons = g_grapes * bananas;
    s_spuds += m_melons;
}

Il faudra peut-être s’y habituer au début, mais qu’en est-il de la programmation? Il y a eu un jour où même {et} vous a semblé étrange. Et une fois que vous vous y êtes habitués, ils vous aident à comprendre le code beaucoup plus rapidement.

(Utiliser "this->" à la place de m_ est logique, mais est encore plus fastidieux et perturbe visuellement. Je ne vois pas cela comme une bonne alternative pour baliser toutes les utilisations des variables membres.)

Une objection possible à l'argument ci-dessus serait d'élargir l'argument aux types. Il peut également être vrai que connaître le type d'une variable "est absolument essentiel pour comprendre le sens de la variable". Si tel est le cas, pourquoi ne pas ajouter à chaque nom de variable un préfixe identifiant son type? Avec cette logique, vous vous retrouvez avec la notation hongroise. Mais beaucoup de gens trouvent la notation hongroise laborieuse, laide et inutile.

void Foo::bar( int iApples )
{
    int iBananas = iApples + g_fGrapes;
    m_fMelons = g_fGrapes * iBananas;
    s_dSpuds += m_fMelons;
}

Hongrois ne / nous dit quelque chose de nouveau sur le code. Nous comprenons maintenant qu’il existe plusieurs conversions implicites dans la fonction Foo :: bar (). Le problème avec le code actuel est que la valeur des informations ajoutées par les préfixes hongrois est petite par rapport au coût visuel. Le système de types C++ inclut de nombreuses fonctionnalités pour aider les types à bien fonctionner ensemble ou pour générer un avertissement ou une erreur du compilateur. Le compilateur nous aide à gérer les types - nous n’avons pas besoin de notation pour le faire. Nous pouvons facilement en déduire que les variables de Foo :: bar () sont probablement numériques, et si c'est tout ce que nous savons, c'est assez bon pour acquérir une compréhension générale de la fonction. Par conséquent, l'intérêt de connaître le type précis de chaque variable est relativement faible. Pourtant, la laideur d'une variable telle que "s_dSpuds" (ou même simplement "dSpuds") est géniale. Ainsi, une analyse coûts-avantages rejette la notation hongroise, tandis que les avantages de g_, s_ et m_ dépassent les coûts aux yeux de nombreux programmeurs.

10
OldPeculier

Je ne peux pas dire à quel point c'est large, mais personnellement, j'ai toujours (et j'ai toujours) préfixé mes variables membres avec 'm'. Par exemple.:

class Person {
   .... 
   private:
       std::string mName;
};

C’est la seule forme de préfixe que j’utilise (je suis très opposé à la notation hongroise), mais elle m’a été très utile au fil des ans. En passant, je déteste généralement l’utilisation des traits de soulignement dans les noms (ou n’importe où ailleurs), mais je fais une exception pour les noms de macro du préprocesseur, car ils sont tous en majuscules.

10
anon

La raison principale pour un préfixe de membre est de faire la distinction entre une fonction membre locale et une variable membre portant le même nom. Ceci est utile si vous utilisez des accesseurs avec le nom de la chose.

Considérer:

class person
{
public:
    person(const std::string& full_name)
        : full_name_(full_name)
    {}

    const std::string& full_name() const { return full_name_; }
private:
    std::string full_name_;
};

La variable membre ne peut pas s'appeler nom_complet dans ce cas. Vous devez renommer la fonction membre en get_full_name () ou décorer la variable membre en quelque sorte.

8
matthewinrandwick

Certaines réponses se concentrent sur la refactorisation, plutôt que sur les conventions de dénomination, comme moyen d'améliorer la lisibilité. Je ne pense pas que l'un puisse remplacer l'autre.

J'ai connu des programmeurs qui ne sont pas à l'aise avec l'utilisation de déclarations locales; ils préfèrent placer toutes les déclarations en haut d'un bloc (comme en C), afin de savoir où les trouver. J'ai constaté que, lorsque la portée le permet, la déclaration des variables à leur première utilisation réduit le temps que je passe à regarder en arrière pour trouver les déclarations. (Ceci est vrai pour moi même pour les petites fonctions.) Cela facilite la compréhension du code que je regarde.

J'espère que la relation avec les conventions de nommage des membres est suffisamment claire: lorsque les membres sont préfixés uniformément, je n'ai jamais à regarder en arrière; Je sais que la déclaration ne sera même pas trouvée dans le fichier source.

Je suis sûr que je n'ai pas commencé à préférer ces styles. Cependant, au fil du temps, travaillant dans des environnements où ils étaient utilisés régulièrement, j'ai optimisé ma pensée pour en tirer parti. Je pense qu'il est possible que beaucoup de gens qui se sentent mal à l'aise avec eux en viennent également à les préférer, compte tenu d'un usage constant.

6
Dan Breslau

D'autres essaient d'appliquer en utilisant this-> membre chaque fois qu'un membre la variable est utilisée

C'est généralement car il n'y a pas de préfixe . Le compilateur a besoin d'assez d'informations pour résoudre la variable en question, qu'il s'agisse d'un nom unique en raison du préfixe ou du mot clé this.

Donc, oui, je pense que les préfixes sont toujours utiles. Personnellement, je préférerais taper '_' pour accéder à un membre plutôt que 'this->'.

6
Kent Boogaart

Je ne pense pas qu'une syntaxe ait une valeur réelle par rapport à une autre. Comme vous l'avez mentionné, tout se résume à l'uniformité des fichiers source.

Le seul point où je trouve de telles règles intéressantes est lorsque j'ai besoin de 2 choses nommées à l'identique, par exemple:

void myFunc(int index){
  this->index = index;
}

void myFunc(int index){
  m_index = index;
}

Je l'utilise pour différencier les deux. De plus, lorsque j'emballe des appels, comme à partir d'une dll Windows, RecvPacket (...) de la Dll peut être encapsulé dans RecvPacket (...) dans mon code. Dans ces cas particuliers, l’utilisation d’un préfixe tel que "_" peut rendre les deux éléments identiques, ce qui est facile à identifier, mais différent pour le compilateur.

6
Eric

Ces conventions ne sont que cela. La plupart des magasins utilisent des conventions de code pour faciliter la lisibilité du code. Ainsi, tout le monde peut facilement consulter un code et déchiffrer rapidement des éléments tels que des membres publics ou privés.

5
Mr. Will

D'autres langues utilisent des conventions de codage, elles ont simplement tendance à être différentes. C # par exemple a probablement deux styles différents que les gens ont tendance à utiliser, l'une des méthodes C++ (_variable, mVariable ou un autre préfixe tel que la notation hongroise), ou ce que j'appelle la méthode StyleCop.

private int privateMember;
public int PublicMember;

public int Function(int parameter)
{
  // StyleCop enforces using this. for class members.
  this.privateMember = parameter;
}

En fin de compte, cela devient ce que les gens savent et ce qui est le mieux. Personnellement, je pense que le code est plus lisible sans la notation hongroise, mais il peut devenir plus facile de trouver une variable avec intellisense par exemple si la notation hongroise est jointe.

Dans mon exemple ci-dessus, vous n'avez pas besoin d'un préfixe m pour les variables membres, car vous préfixez votre utilisation avec ceci. indique la même chose dans une méthode appliquée par le compilateur.

Cela ne signifie pas nécessairement que les autres méthodes sont mauvaises, les gens s'en tiennent à ce qui fonctionne.

4
Will Eddins

L'idée initiale des préfixes sur les variables membres C++ était de stocker des informations de type supplémentaires que le compilateur ignorait. Ainsi, par exemple, vous pourriez avoir une chaîne ayant une longueur fixe de caractères et une autre variable et terminée par un '\ 0'. Pour le compilateur, ils sont tous les deux char *, mais si vous essayez de copier de l'un à l'autre, vous vous exposez à de gros problèmes. Alors, par coeur,

char *aszFred = "Hi I'm a null-terminated string";
char *arrWilma = {'O', 'o', 'p', 's'};

où "asz" signifie que cette variable est "chaîne ascii (terminée par un zéro) et" arr "signifie que cette variable est un tableau de caractères.

Alors la magie opère. Le compilateur sera parfaitement satisfait de cette déclaration:

strcpy(arrWilma, aszFred);

Mais vous, en tant qu'être humain, pouvez l'examiner et dire "hé, ces variables ne sont pas vraiment du même type, je ne peux pas le faire".

Malheureusement, beaucoup d'endroits utilisent des standards tels que "m_" pour les variables membres, "i" pour les entiers, peu importe leur utilisation, "cp" pour les pointeurs de caractères. En d'autres termes, ils dupliquent ce que le compilateur sait et rendent le code difficile à lire en même temps. Je crois que cette pratique pernicieuse devrait être interdite par la loi et être passible de lourdes peines.

Enfin, il y a deux points que je devrais mentionner:

  • Une utilisation judicieuse des fonctionnalités C++ permet au compilateur de connaître les informations à coder dans des variables brutes de type C. Vous pouvez créer des classes qui n'autoriseront que des opérations valides. Cela devrait être fait autant que pratique.
  • Si vos blocs de code sont si longs que vous oubliez le type d'une variable avant de les utiliser, ils sont trop longs pour way. N'utilisez pas de noms, réorganisez-vous.
3
A. L. Flanagan

Notre projet a toujours utilisé "son" comme préfixe pour les données de membre et "le" comme préfixe pour les paramètres, sans préfixe pour les variables locales. C'est un peu mignonne, mais il a été adopté par les premiers développeurs de notre système car ils l'ont vu utilisé comme convention par certaines bibliothèques sources commerciales que nous utilisions à l'époque (XVT ou RogueWave - peut-être les deux). Donc, vous obtiendrez quelque chose comme ceci:

void
MyClass::SetName(const RWCString &theName)
{
   itsName = theName;
}

La principale raison pour laquelle je vois des préfixes de portée (et aucun autre - je déteste la notation hongroise) est que cela vous évite des problèmes en écrivant du code dans lequel vous pensez faire référence à une variable, mais vous faites réellement référence à une autre variable. avec le même nom défini dans la portée locale. Cela évite également de créer des noms de variables pour représenter le même concept, mais avec des portées différentes, comme dans l'exemple ci-dessus. Dans ce cas, vous devrez quand même trouver un préfixe ou un nom différent pour le paramètre "theName" - pourquoi ne pas créer une règle cohérente qui s'applique partout.

Utiliser simplement ceci -> n'est pas vraiment suffisant - nous ne sommes pas aussi intéressés par la réduction des ambiguïtés que par les erreurs de codage, et masquer des noms avec des identifiants localisés peut être un problème. Certes, certains compilateurs peuvent avoir la possibilité de générer des avertissements dans les cas où vous avez masqué le nom dans un champ plus large, mais ces avertissements peuvent devenir gênants si vous travaillez avec un grand nombre de bibliothèques tierces qui ont choisi noms des variables inutilisées qui entrent parfois en collision avec les vôtres.

En ce qui concerne son/le lui-même - je trouve honnêtement qu'il est plus facile de taper que des caractères de soulignement (en tant que dactylographe, j'évite autant que possible les caractères de soulignement - trop d'étirement des lignes de base), et je le trouve plus lisible qu'un soulignement mystérieux.

3
Steve Broberg

Comme d'autres l'ont déjà dit, il est important d'être familier (adapter les styles et les conventions de dénomination à la base de code dans laquelle vous écrivez) et d'être cohérent.

Pendant des années, j'ai travaillé sur une base de code volumineuse qui utilise à la fois la convention "this->" et la notation postfix underscore pour les variables membres. Au fil des années, j'ai également travaillé sur des projets plus petits, dont certains n'avaient aucune sorte de convention pour nommer les variables membres, et d'autres qui avaient des conventions différentes pour nommer les variables membres. Parmi ces petits projets, j'ai toujours trouvé que ceux qui n'avaient pas de convention étaient les plus difficiles à comprendre rapidement.

Je suis très anxieuse à propos du nommage. Je vais me torturer pour que le nom soit attribué à une classe ou à une variable au point que, si je ne parviens pas à trouver quelque chose que je pense être "bon", je choisirai de le nommer quelque chose de stupide et de fournir un commentaire décrivant ce qu'il a réellement est. De cette façon, au moins le nom signifie exactement ce que je veux dire, rien de plus et rien de moins. Et souvent, après l’avoir utilisé pendant un petit moment, je découvre ce que le nom devrait vraiment être et peut revenir en arrière et modifier ou refactoriser de manière appropriée.

Un dernier point sur le sujet d'un IDE effectuant le travail - c'est bien beau, mais les IDE ne sont souvent pas disponibles dans les environnements où j'ai effectué le travail le plus urgent. Parfois, la seule chose disponible à ce stade est une copie de "vi". En outre, j'ai souvent rencontré des cas dans lesquels l'achèvement de code IDE avait propagé une stupidité telle qu'une orthographe incorrecte dans les noms. Ainsi, je préfère ne pas avoir à compter sur une béquille IDE.

3
Chris Cleeland

Lorsque vous avez une grosse méthode ou des blocs de code, il est pratique de savoir immédiatement si vous utilisez une variable locale ou un membre. c'est pour éviter les erreurs et pour plus de clarté!

3
Matthieu

OMI, c'est personnel. Je ne mets pas de préfixes du tout. Quoi qu'il en soit, si le code est censé être public, je pense qu'il devrait mieux comporter des préfixes, afin qu'il puisse être plus lisible.

Souvent, les grandes entreprises utilisent ses propres règles de développement.
Btw, le plus drôle et le plus intelligent que j'ai jamais vu était DRY KISS (Ne vous répétez pas. Restez simple, stupide). :-)

3
Andrejs Cainikovs

Je pense que si vous avez besoin de préfixes pour distinguer les membres de la classe des paramètres de la fonction membre et des variables locales, la fonction est trop grande ou les variables sont mal nommées. Si cela ne tient pas à l'écran pour que vous puissiez facilement voir ce qui est quoi, refactor. 

Étant donné qu'elles sont souvent déclarées loin de l'endroit où elles sont utilisées, je trouve que les conventions de dénomination pour les constantes globales (et les variables globales, bien que l'OMI soit rarement utilisée) ont du sens. Mais sinon, je ne vois pas grand besoin. 

Cela dit, j'avais l'habitude de mettre un trait de soulignement à la fin de tous les membres du cours privé. Comme toutes mes données sont privées, cela implique que les membres ont un tiret bas. D'habitude, je ne le fais plus dans les nouvelles bases de code, mais depuis qu'en tant que programmeur, vous travaillez principalement avec du vieux code, je le fais encore souvent. Je ne suis pas sûr si ma tolérance pour cette habitude vient du fait que je le faisais toujours et toujours, ou que cela est vraiment plus logique que le marquage des variables de membre. 

2
sbi

En python, les doubles soulignés sont utilisés pour émuler les membres privés. Pour plus de détails voir cette réponse

2
Konstantin Tenzin

Je l'utilise car Intellisense de VC++ ne sait pas quand montrer les membres privés lors de l'accès en dehors de la classe. La seule indication est un petit symbole "verrouillé" sur l'icône de champ dans la liste Intellisense. Cela facilite simplement l'identification des membres privés (champs). Aussi une habitude de C # pour être honnête.

class Person {
   std::string m_Name;
public:
   std::string Name() { return m_Name; }
   void SetName(std::string name) { m_Name = name; }
};

int main() {
  Person *p = new Person();
  p->Name(); // valid
  p->m_Name; // invalid, compiler throws error. but intellisense doesn't know this..
  return 1;
}
2
Zack

Il est utile de différencier les variables membres des variables locales en raison de la gestion de la mémoire. De manière générale, les variables membres allouées au tas doivent être détruites dans le destructeur, alors que les variables locales allouées au tas doivent être détruites dans cette étendue. L'application d'une convention de dénomination aux variables membres facilite la gestion correcte de la mémoire.

1
frankster

Vous ne devriez jamais avoir besoin d'un tel préfixe. Si un tel préfixe vous offre un avantage, votre style de codage en général doit être corrigé, et ce n'est pas le préfixe qui empêche votre code d'être clair. Les noms de variables incorrects typiques incluent "autre" ou "2". Vous ne corrigez pas cela en exigeant qu'elle soit mutée, vous le résolvez en faisant réfléchir le développeur à ce que fait cette variable dans le contexte de cette fonction. Peut-être voulait-il dire remoteSide, ou newValue, ou secondTestListener ou quelque chose de ce type.

C'est un anachronisme efficace qui se propage encore trop loin. Arrêtez de préfixer vos variables et donnez-leur des noms propres dont la clarté reflète la durée d'utilisation. Jusqu'à 5 lignes, vous pouvez l'appeler "i" sans confusion; au-delà de 50 lignes, vous avez besoin d'un nom assez long. 

1
dascandy

Je n'utilise presque jamais de préfixes devant mes noms de variables. Si vous utilisez assez IDE suffisamment, vous devriez pouvoir refactoriser et trouver facilement les références. J'utilise des noms très clairs et je n'ai pas peur d'avoir de longs noms de variables. Je n'ai jamais eu de problème avec scope avec cette philosophie.

La seule fois où j'utilise un préfixe serait sur la ligne de signature. Je préfixerai les paramètres d'une méthode avec _ afin de pouvoir programmer de manière défensive autour d'eux.

1
James

J'utilise m_ pour les variables membres simplement pour tirer parti d'Intellisense et des fonctionnalités IDE associées. Lorsque je code l’implémentation d’une classe, je peux taper m_ et voir la liste déroulante avec tous les membres m_ regroupés. 

Mais je pourrais vivre sans m_s sans problème, bien sûr. C'est juste mon style de travail.

1
Hernán

J'aime que les noms de variables ne donnent qu'un sens aux valeurs qu'elles contiennent et laissent la manière dont elles sont déclarées/implémentées en dehors du nom. Je veux savoir ce que signifie la valeur, point final. J'ai peut-être fait plus que la moyenne de refactorisation, mais je trouve que l'intégration de la manière dont quelque chose est implémenté dans le nom rend la refactorisation plus fastidieuse qu'elle ne devrait l'être. Les préfixes indiquant où et comment les membres d'objet sont déclarés sont spécifiques à l'implémentation.

color = Red;

La plupart du temps, je me moque de savoir si Red est une énumération, une structure ou autre, et si la fonction est si grande que je ne me souviens plus si color a été déclaré localement ou est membre, il est probablement temps de rompre la fonction en unités logiques plus petites.

Si votre complexité cyclomatic est si grande que vous ne pouvez pas garder une trace de ce qui se passe dans le code sans indices spécifiques à l'implémentation intégrés dans les noms, il est fort probable que vous deviez réduire la complexité de votre fonction/méthode.

La plupart du temps, je n'utilise «this» que dans les constructeurs et les initialiseurs. 

1
ChrisG65

Code Complete recommande m_varname pour les variables membres.

Bien que je n’aie jamais pensé que la notation m_ soit utile, j’accorderais l’opinion de McConnell à la construction d’une norme.

1
Paul Nathan

Beaucoup de ces conventions datent d'une époque sans éditeurs sophistiqués. Je recommanderais d'utiliser un IDE correct qui vous permet de colorer tous les types de variables. La couleur est de loin plus facile à repérer que n'importe quel préfixe.

Si vous avez besoin d'obtenir encore plus de détails sur une variable, tout IDE moderne devrait pouvoir vous la montrer en déplaçant le curseur ou le curseur dessus. Et si vous utilisez une variable de manière incorrecte (par exemple un pointeur avec l'opérateur.), Vous obtiendrez de toute façon une erreur.

0
Elias Mueller

Selon les normes de codage du véhicule C++ C++ du véhicule aérien interarmées de combat aérien (décembre 2005):

Règle AV 67

Les données publiques et protégées ne doivent être utilisées que dans structs - pas des classes. Justification: Une classe est capable de maintenir son invariant en contrôlant l'accès à ses données. Cependant, une classe ne peut pas contrôler l'accès à ses membres si ces membres sont non privés. De là tout les données d'une classe doivent être privées.

Ainsi, le préfixe "m" devient inutile car toutes les données doivent être privées.

Mais il est bon d’utiliser le préfixe p devant un pointeur car c’est une variable dangereuse.