Toutes les autres réponses défendent la règle 3 de votre professeur.
Permettez-moi de dire que je suis d'accord avec vous : la règle est redondante et je ne la conseillerais pas. Il est vrai que cela théoriquement prévient les erreurs si vous ajoutez toujours des accolades. D'un autre côté, je n'ai jamais rencontré ce problème dans la vraie vie : contrairement à ce que les autres réponses laissent entendre, je n'ai jamais oublié d'ajouter les accolades une fois qu'elles sont devenues nécessaires. Si vous utilisez une indentation correcte, il devient immédiatement évident que vous devez ajouter des accolades une fois que plus d'une instruction est indentée.
La réponse de Component 10 met en lumière le seul cas concevable où cela pourrait vraiment conduire à une erreur. Mais d'un autre côté, remplacer du code via une expression régulière exige toujours énormément de soin de toute façon.
Penchons-nous maintenant sur l'autre côté de la médaille : y a-t-il un désavantage à utiliser toujours des accolades ? Les autres réponses ignorent simplement ce point. Mais il y a un inconvénient : cela prend beaucoup d'espace vertical à l'écran, et cela peut rendre votre code illisible car cela signifie que vous devez défiler plus que nécessaire.
Considérez une fonction avec beaucoup de clauses de garde au début (et oui, ce qui suit est un code C++ horrible mais dans d'autres langages, cela serait une situation assez courante) :
void some_method(obj* a, obj* b)
{
if (a == nullptr)
{
throw null_ptr_error("a");
}
if (b == nullptr)
{
throw null_ptr_error("b");
}
if (a == b)
{
throw logic_error("Impossible de faire une méthode sur des objets identiques");
}
if (not a->precondition_met())
{
throw logic_error("Précondition pour a non respectée");
}
a->do_something_with(b);
}
Il s'agit d'un code horrible, et je soutiens fermement que ce qui suit est beaucoup plus lisible :
void some_method(obj* a, obj* b)
{
if (a == nullptr)
throw null_ptr_error("a");
if (b == nullptr)
throw null_ptr_error("b");
if (a == b)
throw logic_error("Impossible de faire une méthode sur des objets identiques");
if (not a->precondition_met())
throw logic_error("Précondition pour a non respectée");
a->do_something_with(b);
}
De même, les boucles imbriquées courtes bénéficient de l'omission des accolades :
matrix operator +(matrix const& a, matrix const& b) {
matrix c(a.w(), a.h());
for (auto i = 0; i < a.w(); ++i)
for (auto j = 0; j < a.h(); ++j)
c(i, j) = a(i, j) + b(i, j);
return c;
}
Comparez avec :
matrix operator +(matrix const& a, matrix const& b) {
matrix c(a.w(), a.h());
for (auto i = 0; i < a.w(); ++i)
{
for (auto j = 0; j < a.h(); ++j)
{
c(i, j) = a(i, j) + b(i, j);
}
}
return c;
}
Le premier code est concis ; le second code est gonflé.
Et oui, cela peut être atténué dans une certaine mesure en plaçant l'accolade ouvrante sur la ligne précédente. Donc : si vous insistez sur les accolades, au moins mettez l'accolade ouvrante sur la ligne précédente.
En résumé : ne rédigez pas un code inutile qui prend de l'espace à l'écran.
Depuis que j'ai écrit la réponse originale, j'ai principalement accepté le style de code prédominant et j'utilise des accolades sauf si je peux mettre l'ensemble de l'instruction unique sur la ligne précédente. Je maintiens toujours que ne pas utiliser des accolades redondantes est généralement plus lisible, et je n'ai toujours jamais rencontré de bug causé par cela.
265 votes
Lisibilité et maintenabilité. Il n'est pas immédiatement évident à quel bloc de déclaration appartient 'j++', et qu'ajouter du code après ne sera pas associé à l'instruction if.
30 votes
J'ai toujours été conseillé d'utiliser les accolades {} pour ces lignes pour quelques raisons. Cela rend le code plus clair à lire. De plus, quelqu'un d'autre dans six mois pourrait avoir besoin de modifier votre code, donc la clarté est importante et avec les accolades, une erreur est moins susceptible de se produire. Il n'y a rien de techniquement plus correct à ce sujet, c'est plus une question de bonne pratique. Gardez à l'esprit qu'un projet peut contenir des milliers et des milliers de lignes de code pour un nouveau venu à parcourir!
4 votes
Je le fais parce que le code change, et ça m'agace quand je dois ajouter des accolades pour ajouter une ligne de code. "Toujours" est un mot fort, cependant.
5 votes
Je ne pense pas que
5. Utilisez unsigned pour les variables qui sont >= 0
soit un bon conseil. Si vous décrémentez un unsigned int == 0, vous obtiendrez une sous-déclaration. Ce qui peut facilement se produire.30 votes
Je ne suis pas d'accord avec 6, car cela masquera une double suppression et potentiellement cachera des erreurs logiques.
12 votes
Je ne me préoccuperais pas du point 4. Il est étrange de lire le code qu'il produit, et il ne capture qu'une petite classe d'erreurs, que tout compilateur devrait de toute façon signaler.
29 votes
5 pourrait être délicat - considérez cette boucle :
for (unsigned i = 100; i >= 0; --i)
.38 votes
Au fait,
(i % 2 == 0)
contredit (2). Vous vous fiez à la précédence des opérateurs, et le sens est bien sûr((i % 2) == 0)
plutôt que(i % (2 == 0))
. Je classerais la règle 2 comme "un sentiment valide mais 'toujours' est faux".8 votes
@Archie : dans cette boucle,
i
n'est pas "une variable qui est >= 0". C'est >=0 sauf quand il est prévu de devenir négatif pour terminer la boucle. Donc il ne devrait pas être non signé, et la règle 5 ne dit pas qu'il devrait être non signé :-)1 votes
@Federico Il s'agit essentiellement d'éviter une suppression double. Cette approche a ses avantages et inconvénients. Ce post devrait vous donner plus de détails: Est-ce une bonne pratique de mettre un pointeur à NULL après l'avoir supprimé?
7 votes
@TheForestAndtheTrees : "Il n'est pas immédiatement évident à quel bloc de déclaration appartient 'j++'..." Dites ça à Guido van Rossum.
20 votes
"Ne jamais se fier à la priorité des opérateurs" implique que, au moins, l'expression
i % 2 == 0
nécessite des parenthèses :(i % 2) == 0
. Et, bien sûr, si vous faites quelque chose commei = 2 * j + 1;
vous avez besoin de plusieurs parenthèses :i = ((2 * j) + 1);
. Votre code finirait par ressembler à du LISP (Lots of Infernal Stupid Parentheses. Pas que ce soit ce dont vous avez demandé, mais les règles stupides mènent à un code stupide. Certaines de ces lignes directrices sont des solutions de codage pour les erreurs des débutants. La plupart des programmeurs ne restent pas débutants longtemps, et suivre les règles des débutants n'est pas une bonne idée. Apprenez à écrire du code correct.1 votes
Comme vous ne l'avez pas dit autrement, il faut supposer que nous parlons de la version actuelle de C++ et en tant que tel, vous ne devez pas utiliser de pointeurs bruts sans bonne raison, et si vous le faites, lors du réglage d'un "état non valide", définissez-le sur
null_ptr
7 votes
Vous ne devez jamais utiliser
delete
en C++ moderne. Utilisez toujours des pointeurs intelligents à la place.3 votes
@ron:
std::can.open()
:)10 votes
La raison la plus populaire semble être que "vous pouvez oublier d'ajouter des crochets lorsque vous décidez plus tard d'ajouter d'autres instructions au corps de la boucle". Cependant, je pense que c'est une exagération. Pratiquement tous les IDE que j'ai utilisés corrigent automatiquement l'indentation de toute façon et de telles choses ressortiraient comme un bouton de fièvre.
6 votes
Je trouve remarquable qu'une question avec autant de réponses et de commentaires (y compris ceux sur les réponses) que personne n'a noté : le public principal du code est d'autres programmeurs et vous-même. Possiblement dans de nombreuses années avec peu de connaissances de ce système et des hypothèses que vous appliquez. Partez toujours du principe que quelqu'un lisant votre code est contre des délais majeurs (la production est en panne maintenant et coûte des millions de dollars par heure), ils sont psychotiques, et savent où vous vivez.
6 votes
@Richard : malheureusement, dans ces conditions, vous ne pouvez pas gagner. Lorsqu'on doit choisir entre deux règles, il y a un psychotique hypothétique qui est incapable de lire l'une et un autre psychotique hypothétique qui est incapable de lire l'autre. Donc, quelle que soit celle que vous codez, quelqu'un trouvera votre code illisible. Par "trouve illisible", je veux dire, "est prêt à argumenter contre la lisibilité dans le contexte du style idéal", sans nécessairement dire qu'ils ne peuvent réellement le lire.
1 votes
C'est ainsi que votre code fonctionne également en Perl sans modification.
2 votes
Vous dites que c'est du C++? Les objets appellent automatiquement les destructeurs lorsqu'ils sortent de leur portée... donc mettre des variables locales dans leurs blocs les libère à la fin de leur bloc, ce qui est une bonne chose à avoir en C++.
8 votes
Je ne comprends pas le point 6. J'ai rencontré un bug où j'ai oublié que le pointeur que je supprimais était une ressource partagée. Si je l'avais défini sur NULL après l'avoir supprimé, mon programme aurait continué à s'exécuter en affichant de mauvaises réponses. Parce que je ne l'ai pas défini sur null, il a généré une segmentation d'erreur la fois suivante où je l'ai exécuté, me permettant de trouver le vrai problème. Ne jamais laisser l'ordinateur cacher vos erreurs logiques et les oublier.
0 votes
@SteveJessop Plus un cas hypothétique psychotique mis dans une position très stressante parce que l'énoncé n'était pas dans un bloc et a été manqué lors du test car ce cas limite n'a pas été pris en compte. J'ai été brûlé (mais pas gravement) par cela. (Si quelqu'un - même psychotique - ne peut pas lire de code formaté de manière cohérente dans l'un des styles courants, alors le problème principal est leur manque de capacité). TL/DR: pas une question de placement des accolades mais de leur présence, et je suis d'accord avec la règle n°3 pour ces cas exceptionnels qui sauvent ma peau.
0 votes
@Richard: d'accord, donc ils vont venir me chercher avec une hache s'ils font une erreur en modifiant mon code, mais ils ne viendront pas me chercher avec une hache s'ils considèrent que mes accolades obstruent leur vue de mon code ;-) Comme ça se produit, je mets en pratique les accolades sauf si j'écris
if (condition) statement;
sur une seule ligne, ce qui est rare. Donc je devrais être principalement à l'abri de votre genre de psychopathie, mais pas totalement. Malheureusement, je mets l'accolade ouvrante sur la même ligne, donc RMS m'attrapera de toute façon.2 votes
Je pense que je suis d'accord seulement avec le premier élément de cette liste. Vous devez avoir confiance en la justesse de votre code parce que vous l'avez testé, pas parce que vous avez suivi un ensemble de règles dans l'espoir d'éviter les coquilles.
1 votes
@Luchian Grigore, je suis entièrement d'accord avec vous sur ce point. La double suppression est généralement une erreur logique. À moins qu'un pointeur par conception puisse être nul, on ne devrait pas le définir comme tel.
11 votes
1 est la seule directive ici qui ne soit pas discutable
5 votes
Dans vos exemples, vos boucles doivent être
for (int i = 0 ; 100 > i ; ++i)
(conformément à la Règle 4)...1 votes
@TomTanner La question "suscite un débat, des arguments [...] ou une discussion prolongée". Les réponses ci-dessous en sont la preuve. Mais il y a déjà 4 votes pour sa réouverture, donc soyez assuré que le débat, les arguments et la discussion prolongée reprendront bientôt vie.
1 votes
Il n'y a que OTBS.
4 votes
Ma règle générale concernant les parenthèses est la suivante : si je me demande jamais quel est l'ordre de priorité des opérateurs dans une expression, j'utilise des parenthèses pour le rendre explicite. En particulier, je préfère utiliser des parenthèses pour regrouper des expressions qui utilisent plusieurs opérateurs booléens. Je ne pense pas que quiconque devrait avoir à lire la documentation sur l'ordre de priorité des opérateurs pour lire mon code. Mais si vous ne savez pas comment
x + 5 * y == z
est regroupé, peut-être devriez-vous suivre un cours d'algèbre avant de faire de la programmation.0 votes
@NateC-K Cela dépend en effet de la langue. Je connais au moins une où l'ordre des opérations n'existe pas, donc votre exemple serait effectivement analysé comme
((x + 5) * y) == z
1 votes
"Objet const en côté gauche de la comparaison // OK". Vous voulez dire faire une comparaison comme ça:
if (5 == myVar)
. J'aime Yoda, mais pas autant.0 votes
@Izkata: Si vous programmez en MUMPS ou dans un autre langage similaire, évidemment les règles sont différentes. Mais l'utilisation de parenthèses devient alors une question de nécessité, pas de style, donc ce n'est pas vraiment pertinent pour cette conversation.
0 votes
Et si votre single-line est un
macro
... Hum !1 votes
@EmAdpres: si cela ne fonctionne pas, vous devriez corriger la macro (une macro doit être syntaxiquement soit une expression, soit une seule instruction), au lieu d'ajouter des accolades partout dans votre code.
0 votes
"Quel en est le bénéfice ?" parce que sinon vous vous mettez en danger de commettre une idiotie fatale comme celle-ci : dwheeler.com/essays/apple-goto-fail.html ...ou votre penchant pour un style négligent et laid conduisant à des mésaventures - et ensuite à une incohérence sans intérêt - comme celle-ci : lkml.org/lkml/2015/12/14/461 aussi @Emadpres, les accolades peuvent être utilisées dans des contextes en une seule ligne, de plus vous pouvez artificiellement insérer des sauts de ligne dans les macros en utilisant `\` de toute façon
0 votes
"Objet Constant à gauche de la comparaison // OK" - Je pensais bien. C'est malin, je pensais! Je suis malin, je pensais! J'évite une affectation accidentelle, je pensais! Oui - éviter une affectation accidentelle - tout en inversant la comparaison nécessaire, ne pas toujours réussir à le faire correctement dans la précipitation, et donc parfois inverser le résultat et la réponse. Ce n'est pas vraiment malin, pas assez pour justifier le risque, et cela laisse perplexe la plupart des gens qui le voient - y compris vous-même - probablement parce que cela va à l'encontre de l'intuition et des leçons de mathématiques. C/++ a pris une décision discutable sur
=
vs==
- mais pas assez mauvaise pour justifier cela.1 votes
Je viens de rencontrer un problème concernant les boucles for consécutives (non imbriquées) sur une seule ligne où seule la première boucle itérerait normalement, tandis que les suivantes seraient simplement sautées. Ajouter les crochets ET les sauts de ligne a résolu le problème. Encore une fois, les crochets seuls n'ont pas résolu le problème.