Quel est le but d'utiliser des accolades (ie {}) pour une boucle if ou une seule ligne?

321

Je lis des notes de cours de mon professeur C ++ et il a écrit ce qui suit:

  1. Utiliser l'indentation // OK
  2. Ne vous fiez jamais à la priorité de l'opérateur - Utilisez toujours des parenthèses // OK
  3. Utilisez toujours un bloc {} - même pour une seule ligne // pas OK , pourquoi ???
  4. Objet Const sur le côté gauche de la comparaison // OK
  5. Utilisez non signé pour les variables qui sont> = 0 // bonne astuce
  6. Définissez le pointeur sur NULL après la suppression - Double protection contre la suppression // pas mal

La 3ème technique n'est pas claire pour moi: que gagnerais-je en plaçant une ligne dans un { ... }?

Par exemple, prenez ce code bizarre:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0)
    {
        j++;
    }
}

et remplacez-le par:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
    if (i % 2 == 0)
        j++;

Quel est l'avantage d'utiliser la 1ère version?

JAN
la source
256
Lisibilité et maintenabilité. Il n'est pas immédiatement évident de savoir à quel bloc d'instructions 'j ++' appartient, et que l'ajout de code après qu'il ne sera pas associé à l'instruction if.
The Forest And The Trees
27
On m'a toujours dit d'utiliser les accolades {} pour ces lignes pour plusieurs raisons. Cela rend le code plus clair à lire. De plus, quelqu'un d'autre dans six mois peut avoir besoin de modifier votre code, la clarté est donc importante et avec les accolades, une erreur est moins susceptible de se produire. Il n'y a rien de plus correct sur le plan technique, c'est plutôt une question de bonnes pratiques. Gardez à l'esprit qu'un projet peut avoir des milliers et des milliers de lignes de code pour un nouveau type à parcourir!
RossC
30
Je ne suis pas d'accord avec 6, car cela masquera une double suppression et masquera potentiellement des erreurs de logique.
Luchian Grigore
28
# 5 pourrait être difficile - considérer cette boucle: for (unsigned i = 100; i >= 0; --i).
Archie
36
Btw, (i % 2 == 0)contredit (2). Vous comptez sur la priorité 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 valable mais" toujours "est faux".
Steve Jessop

Réponses:

509

Essayons également de modifier ilorsque nous incrémentons j:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
    if (i % 2 == 0)
        j++;
        i++;

Oh non! Venant de Python, cela semble correct, mais en fait ce n'est pas le cas, car c'est équivalent à:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
    if (i % 2 == 0)
        j++;
i++;

Bien sûr, c'est une erreur stupide, mais que même un programmeur expérimenté pourrait faire.

Une autre très bonne raison est indiquée dans la réponse de ta.speot.is .

Un troisième qui me vient à l'esprit est celui des emboîtements if:

if (cond1)
   if (cond2) 
      doSomething();

Supposons maintenant que vous voulez maintenant doSomethingElse()quand il cond1n'est pas respecté (nouvelle fonctionnalité). Alors:

if (cond1)
   if (cond2) 
      doSomething();
else
   doSomethingElse();

ce qui est évidemment faux, car les elseassociés à l'intérieur if.


Edit: Étant donné que cela attire l'attention, je vais clarifier mon point de vue. La question à laquelle je répondais est:

Quel est l'avantage d'utiliser la 1ère version?

Ce que j'ai décrit. Il y a quelques avantages. Mais, OMI, les règles "toujours" ne s'appliquent pas toujours. Je ne soutiens donc pas entièrement

Utilisez toujours un bloc {} - même pour une seule ligne // pas OK, pourquoi ???

Je ne dis pas toujours utiliser un {}bloc. Si c'est une condition et un comportement assez simples, ne le faites pas. Si vous pensez que quelqu'un pourrait venir plus tard et modifier votre code pour ajouter des fonctionnalités, faites-le.

Luchian Grigore
la source
5
@Science_Fiction: Vrai, mais si vous ajoutez i++ avant j++ , les deux variables seront toujours dans la portée quand elles seront utilisées.
Mike Seymour
26
Cela semble très raisonnable, mais néglige le fait que l'éditeur fasse l'indentation, pas vous, et il indente le i++;d'une manière qui montre immédiatement qu'il ne fait pas partie de la boucle. (Dans le passé, cela aurait pu être un argument raisonnable, et j'ai vu de tels problèmes. Il y a environ 20 ans. Pas depuis.)
James Kanze
43
@James: ce n'est pas un "fait", cependant, c'est votre flux de travail. Et le flux de travail de beaucoup de gens, mais pas de tout le monde. Je ne pense pas que ce soit nécessairement une erreur de traiter la source C ++ comme un fichier texte brut, plutôt que la sortie d'un éditeur WYSIWYG (vi / emacs / Visual Studio) qui applique les règles de formatage. Cette règle est donc indépendante des éditeurs au-delà de ce dont vous avez besoin, mais pas au-delà de ce que les gens utilisent réellement pour éditer C ++. D'où "défensif".
Steve Jessop
31
@JamesKanze Comptez-vous vraiment sur l'hypothèse que tout le monde travaille toujours dans des IDE puissants? Le dernier CI a écrit était à Nano. Même compte tenu de cela, l'une des premières choses que j'ai tendance à désactiver dans un IDE est l'auto-indentation - parce que l'IDE a tendance à entraver mon flux de travail non linéaire , en essayant de corriger mes `` erreurs '' sur la base d'informations incomplètes . Les IDE ne sont pas très bons pour indenter automatiquement le flux naturel de chaque programmeur. Les programmeurs qui utilisent de telles fonctionnalités ont tendance à fusionner leur style avec leur IDE, ce qui est bien si vous n'utilisez qu'un seul IDE, mais pas si vous travaillez sur plusieurs.
Rushyo
10
"C'est une erreur stupide, mais que même un programmeur expérimenté pourrait faire." - Comme je l'ai dit dans ma réponse, je n'y crois pas. Je pense que c'est un cas entièrement artificiel qui ne pose pas de problème en réalité.
Konrad Rudolph
324

Il est très facile de modifier accidentellement le flux de contrôle avec des commentaires si vous n'utilisez pas {et }. Par exemple:

if (condition)
  do_something();
else
  do_something_else();

must_always_do_this();

Si vous commentez do_something_else()avec un commentaire sur une seule ligne, vous vous retrouverez avec ceci:

if (condition)
  do_something();
else
  //do_something_else();

must_always_do_this();

Il compile, mais must_always_do_this()n'est pas toujours appelé.

Nous avions ce problème dans notre base de code, où quelqu'un était entré pour désactiver certaines fonctionnalités très rapidement avant la sortie. Heureusement, nous l'avons pris en revue de code.

ta.speot.is
la source
3
Ohh mon garçon !! c'est un comportement défini qui must_always_do_this();s'exécutera si vous commentez // do_something_else ();
Mr.Anubis
1
@Supr, comme il a été écrit pour la première fois, il dit qu'il est difficile de casser le flux correct si vous utilisez des accolades, puis donne un exemple de la façon dont il est facile de casser sans avoir le code correctement entre crochets
SeanC
20
Je suis tombé sur ça juste l'autre jour. if(debug) \n //print(info);. Fondamentalement, a sorti une bibliothèque entière.
Kevin
4
Fortunately we caught it in code review.Aie! Cela semble si mal. Fortunately we caught it in unit tests.serait bien mieux!
BЈовић
4
@ BЈовић Mais que faire si le code était dans un test unitaire? L'esprit s'embrouille. (Je plaisante, c'est une application héritée. Il n'y a pas de tests unitaires.)
ta.speot.is
59

J'ai des doutes quant à la compétence du professeur. Compte tenu de ses points:

  1. D'accord
  2. Quelqu'un voudrait-il vraiment écrire (ou vouloir lire) (b*b) - ((4*a)*c)? Certaines priorités sont évidentes (ou devraient l'être), et les parenthèses supplémentaires ne font qu'ajouter à la confusion. (D'un autre côté, vous devez utiliser les parenthèses dans des cas moins évidents, même si vous savez qu'elles ne sont pas nécessaires.)
  3. Sorte de. Il existe deux conventions très répandues pour le formatage des conditionnelles et des boucles:
    if (cond) {
        code;
    }
    
    et:
    if (cond)
    {
        code;
    }
    
    Dans le premier, je serais d'accord avec lui. L'ouverture {n'est pas si visible, il est donc préférable de supposer qu'elle est toujours là. Dans le second, cependant, je (et la plupart des gens avec qui j'ai travaillé) n'ai aucun problème à omettre les accolades pour une seule déclaration. (À condition, bien sûr, que l'indentation soit systématique et que vous utilisiez ce style de manière cohérente. (Et beaucoup de très bons programmeurs, écrivant du code très lisible, omettent les accolades même lors du formatage de la première façon.)
  4. Non . Des choses comme if ( NULL == ptr )sont assez laides pour nuire à la lisibilité. Écrivez les comparaisons de manière intuitive. (Ce qui dans de nombreux cas entraîne la constante à droite.) Son 4 est un mauvais conseil; tout ce qui rend le code non naturel le rend moins lisible.
  5. Non . N'importe quoi mais intest réservé pour des cas particuliers. Pour les programmeurs C et C ++ expérimentés, l'utilisation d' unsignedopérateurs de bits de signaux. C ++ n'a pas de véritable type cardinal (ou tout autre type de sous-gamme efficace); unsignedne fonctionne pas pour les valeurs numériques, en raison des règles de promotion. Des valeurs numériques sur lesquelles aucune opération arithmétique n'aurait de sens, comme les numéros de série, pourraient vraisemblablement l'être unsigned. Je m'y opposerais cependant, car il envoie le mauvais message: les opérations au niveau du bit n'ont pas de sens non plus. La règle de base est que les types intégraux sont int, à moins qu'il n'y ait une raison importante d'utiliser un autre type.
  6. Non . Faire cela systématiquement est trompeur et ne protège en fait contre rien. Dans le strict code OO, delete this;est souvent le cas le plus fréquent (et vous ne pouvez pas mettre thisà NULL), et par ailleurs, la plupart deletesont en Destructeurs, de sorte que vous ne pouvez pas accéder au pointeur plus tard de toute façon. Et le définir NULLne fait rien pour les autres pointeurs flottant. Régler systématiquement le pointeur sur NULLdonne un faux sentiment de sécurité et ne vous rapporte vraiment rien.

Regardez le code dans l'une des références typiques. Stroustrup viole toutes les règles que vous avez données, à l'exception de la première, par exemple.

Je vous suggère de trouver un autre conférencier. Celui qui sait de quoi il parle.

James Kanze
la source
13
Le chiffre 4 peut être moche, mais il a un but. Il essaie d'empêcher if (ptr = NULL). Je ne pense pas avoir déjà utilisé delete this, est-ce plus courant que ce que j'ai vu? Je n'ai pas tendance à penser que la définition d'un pointeur sur NULL après utilisation est une mauvaise chose à faire, mais YMMV. C'est peut-être juste moi, mais la plupart de ses directives ne semblent pas si mauvaises.
Firedragon
16
@Firedragon: La plupart des compilateurs avertiront à if (ptr = NULL)moins que vous ne l' écriviez comme if ((ptr = NULL)). Je dois être d'accord avec James Kanze que la laideur d'avoir d' NULLabord en fait un NON définitif pour moi.
Leo
34
@JamesKanze: Je dois dire que je suis en désaccord avec la plupart de ce que vous avez déclaré ici - bien que j'apprécie et respecte vos arguments pour y parvenir. Pour les programmeurs C et C ++ expérimentés, l'utilisation d'opérateurs de bits de signaux non signés. - Je ne suis pas du tout d'accord: l'utilisation d' opérateurs de bits signale l'utilisation d'opérateurs de bits. Pour moi, l'utilisation de unsignedindique une aspiration de la part du programmeur que la variable ne devrait représenter que des nombres positifs. Le mélange avec des numéros signés provoquera généralement un avertissement du compilateur, ce qui était probablement ce que le conférencier avait l'intention.
Composante 10
18
Pour les programmeurs C et C ++ expérimentés, l'utilisation d'opérateurs de bits de signaux non signés Ou non. size_t, n'importe qui?
ta.speot.is
16
@James Kanze, considérez le but. Vous comparez le code produit par un programmeur expérimenté à des exemples pédagogiques. Ces règles sont fournies par l'enseignant car ce sont les types d'erreurs qu'il voit commettre par ses élèves. Avec l'expérience, les étudiants peuvent se détendre ou ignorer ces absolus.
Joshua Shane Liberman
46

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 le conseillerais pas. Il est vrai que cela empêche théoriquement 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 d'autres réponses impliquent, je n'ai pas oublié une fois d'ajouter les accolades une fois qu'elles sont devenues nécessaires. Si vous utilisez une indentation appropriée, il devient immédiatement évident que vous devez ajouter des accolades une fois plus d'une instruction mise en retrait.

La réponse du «Composant 10» met en évidence le seul cas envisageable où cela pourrait vraiment conduire à une erreur. Mais d'un autre côté, le remplacement de code via une expression régulière mérite toujours un énorme soin.

Regardons maintenant l'autre côté de la médaille: y a-t-il un inconvénient à toujours utiliser 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, ce qui peut rendre votre code illisible car cela signifie que vous devez faire 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 mauvais code C ++ mais dans d'autres langages, ce 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("Cannot do method on identical objects");
    }
    if (not a->precondition_met())
    {
        throw logic_error("Precondition for a not met");
    }

    a->do_something_with(b);
}

C'est 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("Cannot do method on identical objects");
    if (not a->precondition_met())
        throw logic_error("Precondition for a not met");

    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;
}

Comparer 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 deuxième code est gonflé.

Et oui, cela peut être atténué dans une certaine mesure en mettant l'accolade d'ouverture sur la ligne précédente. Mais cela serait toujours moins lisible que le code sans accolades.

En bref: n'écrivez pas de code inutile qui occupe de l'espace à l'écran.

Konrad Rudolph
la source
26
Si vous ne croyez pas à l'écriture de code qui occupe inutilement de l'espace à l'écran, alors vous n'avez pas à mettre l'accolade d'ouverture sur sa propre ligne. Je vais probablement devoir maintenant m'éloigner et fuir la sainte vengeance de GNU, mais sérieusement - soit vous voulez que votre code soit compact verticalement, soit vous ne le faites pas. Et si vous le faites, ne faites pas des choses conçues uniquement pour rendre votre code moins compact verticalement. Mais comme vous le dites, après avoir corrigé cela, vous souhaitez également supprimer les accolades redondantes. Ou peut-être simplement écrire if (a == nullptr) { throw null_ptr_error("a"); }en une seule ligne.
Steve Jessop
6
En @ Steve fait, je ne mettre l'accolade d'ouverture sur la ligne précédente, pour la raison que vous avez dit. J'ai utilisé l'autre style ici pour rendre plus évidente à quel point la différence peut être extrême.
Konrad Rudolph
9
+1 Je suis entièrement d'accord que votre premier exemple est beaucoup plus facile à lire sans accolades. Dans le deuxième exemple, mon style de codage personnel consiste à utiliser des accolades sur la boucle for externe et non à l'intérieur. Je ne suis pas d'accord avec @SteveJessop sur le fait d'être un extrême ou l'autre sur le code verticalement compact. J'omets des accolades supplémentaires avec une doublure pour réduire l'espace vertical, mais je mets mes accolades d'ouverture sur une nouvelle ligne parce que je trouve plus facile de voir la portée lorsque les accolades sont alignées. L'objectif est la lisibilité, et parfois cela signifie utiliser plus d'espace vertical, d'autres fois cela signifie utiliser moins.
Travesty3
22
"Je n'ai jamais rencontré ce problème dans la vraie vie": vous êtes chanceux. Des choses comme ça ne vous brûlent pas seulement, elles vous donnent 90% de brûlures au troisième degré (et ce ne sont que quelques couches de gestion exigeant une correction tard dans la soirée).
Richard
9
@ Richard Je n'achète tout simplement pas ça. Comme je l'ai expliqué dans le chat, même si cette erreur devait se produire (ce que je trouve peu probable), il est trivial de la corriger une fois que vous avez regardé la trace de la pile, car il est évident où l'erreur se trouve simplement en regardant le code. Votre affirmation exagérée est complètement sans fondement.
Konrad Rudolph
40

La base de code sur laquelle je travaille est parsemée de code par des personnes ayant une aversion pathologique pour les accolades, et pour les personnes qui viendront plus tard, cela peut vraiment faire une différence dans la maintenabilité.

L'exemple problématique le plus fréquent que j'ai rencontré est le suivant:

if ( really incredibly stupidly massively long statement that exceeds the width of the editor) do_foo;
    this_looks_like_a_then-statement_but_isn't;

Donc, quand je viens et souhaite ajouter une déclaration, je peux facilement me retrouver avec cela si je ne fais pas attention:

if ( really incredibly stupidly massively long statement that exceeds the width of the editor) do_foo;
{
    this_looks_like_a_then-statement_but_isn't;
    i_want_this_to_be_a_then-statement_but_it's_not;
}

Étant donné qu'il faut ajouter ~ 1 seconde accolades et peut vous faire économiser au moins quelques minutes confuses débogage, pourquoi voudriez - vous jamais pas aller avec l'option d' ambiguïté réduite? Cela me semble être une fausse économie.

confiture
la source
16
Le problème dans cet exemple n'est-il pas lié à l'indentation incorrecte et aux lignes trop longues plutôt qu'aux accolades?
Honza Brabec
7
Oui, mais suivre des directives de conception / codage qui ne sont que «sûres» en supposant que les gens suivent également d'autres directives (comme ne pas avoir de lignes trop longues) semble poser problème. Si les accolades avaient été installées depuis le début, il serait impossible de se retrouver avec un bloc if incorrect dans cette situation.
jam
16
Comment ajouter des accolades (en if(really long...editor){ do_foo;}vous aidant à éviter ce cas? Il semblerait que le problème soit toujours le même. Personnellement, je préfère éviter les accolades quand ce n'est pas nécessaire, mais cela n'a rien à voir avec le temps nécessaire pour les écrire mais la lisibilité réduite en raison des deux lignes supplémentaires dans le code.
Grizzly
1
Bon point - je supposais que le recours à des appareils orthodontiques entraînerait également leur mise à un endroit raisonnable, mais bien sûr, quelqu'un déterminé à rendre les choses difficiles pourrait les mettre en ligne comme dans votre exemple. J'imagine que la plupart des gens ne le feraient pas, cependant.
jam
2
La première et dernière chose que je fais lorsque je touche un fichier est d'appuyer sur le bouton de formatage automatique. Il élimine la plupart de ces problèmes.
Jonathan Allen
20

Mon 2c:

Utiliser l'indentation

Évidemment

Ne vous fiez jamais à la priorité de l'opérateur - Utilisez toujours des parenthèses

Je n'utiliserais pas les mots "jamais et" toujours ", mais en général, je vois cette règle utile. Dans certaines langues (Lisp, Smalltalk), ce n'est pas un problème.

Utilisez toujours un bloc {} - même pour une seule ligne

Je ne fais jamais ça et je n'ai jamais eu un seul problème, mais je peux voir comment cela peut être bon pour les étudiants, en particulier. s'ils ont étudié Python avant.

Objet Const sur le côté gauche de la comparaison

Conditions Yoda? Non je t'en prie. Cela nuit à la lisibilité. Utilisez simplement le niveau d'avertissement maximal lorsque vous compilez votre code.

Utilisez non signé pour les variables qui sont> = 0

D'ACCORD. Assez drôle, j'ai entendu Stroustrup en désaccord.

Définissez le pointeur sur NULL après la suppression - Double protection contre la suppression

Mauvais conseil! Ne disposez jamais d'un pointeur pointant vers un objet supprimé ou inexistant.

Nemanja Trifunovic
la source
5
+1 uniquement pour le dernier point. De toute façon, un pointeur brut n'a pas d'entreprise possédant de mémoire.
Konrad Rudolph
2
En ce qui concerne l'utilisation de non signé: non seulement Stroustrup, mais K&R (en C), Herb Sutter et (je pense) Scott Meyers. En fait, je n'ai jamais entendu quelqu'un qui comprenait vraiment les règles du C ++ plaider pour l'utilisation de non signé.
James Kanze
2
@JamesKanze En fait, à la même occasion, j'ai entendu l'opinion de Stroustrup (une conférence de Boston en 2008), Herb Sutter était là et n'était pas d'accord avec Bjarne sur le coup.
Nemanja Trifunovic
3
Juste pour terminer le " unsignedest cassé", l'un des problèmes est que lorsque C ++ compare des types signés et non signés de taille similaire, il se convertit en un non signé avant de faire la comparaison. Ce qui entraîne un changement de valeur. La conversion au signé ne serait pas nécessairement beaucoup mieux; la comparaison doit vraiment avoir lieu "comme si" les deux valeurs étaient converties en un type plus grand qui pourrait représenter toutes les valeurs de l'un ou l'autre type.
James Kanze
1
@SteveJessop Je pense que vous devez le prendre dans le contexte d'un retour de fonction unsigned. Je suis sûr qu'il n'a aucun problème à exp(double)renvoyer une valeur supérieure à MAX_INT:-). Mais encore une fois, le vrai problème, ce sont les conversions implicites. int i = exp( 1e6 );est parfaitement valide C ++. Stroustrup a en fait proposé de déprécier les conversions implicites avec perte à un moment donné, mais le comité n'était pas intéressé. (Une question intéressante: serait unsigned-> intconsidérée comme avec perte. Je considérerais à la fois unsigned-> intet int-> unsignedavec perte. Ce qui contribuerait grandement à faire unsignedOK
James Kanze
18

il est plus intuitif et facilement compréhensible. Cela rend l'intention claire.

Et il garantit que le code ne se casse pas lorsqu'un nouvel utilisateur risque de le manquer sans le savoir {, }tout en ajoutant une nouvelle instruction de code.

Alok Save
la source
Makes the intent clear+1, c'est probablement la raison la plus concise et la plus précise.
Qix - MONICA A ETE BRUTEE le
13

Pour ajouter aux suggestions très sensées ci-dessus, un exemple que j'ai rencontré lors de la refactorisation d'un code où cela devient critique était le suivant: je modifiais une très grande base de code pour passer d'une API à une autre. La première API a été appelée pour définir l'ID d'entreprise comme suit:

setCompIds( const std::string& compId, const std::string& compSubId );

alors que le remplacement a nécessité deux appels:

setCompId( const std::string& compId );
setCompSubId( const std::string& compSubId );

J'ai décidé de changer cela en utilisant des expressions régulières, ce qui a été un grand succès. Nous avons également transmis le code via astyle , ce qui l'a rendu beaucoup plus lisible. Puis, à mi-chemin du processus d'examen, j'ai découvert que, dans certaines circonstances conditionnelles, cela changeait ceci:

if ( condition )
   setCompIds( compId, compSubId );

Pour ça:

if ( condition )
   setCompId( compId );
setCompSubId( compSubId );

ce qui n'est clairement pas ce qui était requis. J'ai dû revenir au début pour le faire à nouveau en traitant le remplacement comme complètement dans un bloc, puis en modifiant manuellement tout ce qui a fini par paraître maladroit (au moins, ce ne serait pas incorrect.)

Je remarque que qu'astyle a maintenant l'option --add-bracketsqui vous permet d'ajouter des parenthèses là où il n'y en a pas et je le recommande fortement si jamais vous vous retrouvez dans la même position que moi.

Composante 10
la source
5
J'ai vu une fois une documentation qui contenait la merveilleuse monnaie "Microsoftligent". Oui, il est possible de faire des erreurs importantes avec la recherche globale et le remplacement. Cela signifie simplement que la recherche globale et le remplacement doivent être utilisés intelligemment, et non de manière microsoft.
Pete Becker
4
Je sais que ce n'est pas mon post-mortem à effectuer, mais si vous allez faire du remplacement de texte sur le code source, vous devez le faire selon les mêmes règles que vous utiliseriez pour le type de remplacement de texte qui est bien établi dans la langue: macros. Vous ne devriez pas écrire une macro #define FOO() func1(); \ func2(); (avec un saut de ligne après la barre oblique inverse), il en va de même pour la recherche et le remplacement. Cela dit, j'ai vu "toujours utiliser des accolades" avancé comme règle de style précisément parce que cela vous évite d'encapsuler toutes vos macros multi-instructions do .. while(0). Mais je ne suis pas d'accord.
Steve Jessop
2
Btw, c'est "bien établi" dans le sens où la renouée japonaise est bien établie: je ne dis pas que nous devrions faire tout notre possible pour utiliser des macros et du remplacement de texte, mais je dis que lorsque nous faisons une telle chose, nous devrions le faire d'une manière qui fonctionne, plutôt que de faire quelque chose qui ne fonctionne que si une règle de style particulière a été imposée avec succès sur toute la base de code :-)
Steve Jessop
1
@SteveJessop On pourrait également plaider pour des bretelles et une ceinture. Si vous devez utiliser de telles macros (et nous l'avons fait, avant C ++ et inline), vous devriez probablement viser à ce qu'elles fonctionnent autant que possible comme une fonction, en utilisant l' do { ... } while(0)astuce si nécessaire (et beaucoup de parenthèses supplémentaires. Mais cela ne ne vous empêche pas d'utiliser des accolades partout, si tel est le style de la maison. (FWIW: J'ai travaillé dans des endroits avec différents styles de maison, couvrant tous les styles discutés ici. Je n'ai jamais trouvé aucun problème sérieux.)
James Kanze
1
Et je pense que plus vous avez travaillé avec de styles, plus vous lisez et éditez le code avec soin. Donc, même si vous avez une préférence pour ce qui est le plus facile à lire, vous pourrez toujours lire avec succès les autres. J'ai travaillé dans une entreprise où différents composants ont été écrits dans différents "styles de maison" par les différentes équipes, et la bonne solution est de s'en plaindre dans la salle à manger sans grand effet, de ne pas essayer de créer un style global :-)
Steve Jessop
8

J'utilise {}partout sauf dans quelques cas où c'est évident. Une seule ligne est l'un des cas:

if(condition) return; // OK

if(condition) // 
   return;    // and this is not a one-liner 

Cela peut vous blesser lorsque vous ajoutez une méthode avant le retour. L'indentation indique que le retour s'exécute lorsque la condition est remplie, mais il reviendra toujours.

Autre exemple en C # avec utilisation de statment

using (D d = new D())  // OK
using (C c = new C(d))
{
    c.UseLimitedResource();
}

ce qui équivaut à

using (D d = new D())
{
    using (C c = new C(d))
    {
        c.UseLimitedResource();
    }
}
Lukasz Madon
la source
1
Utilisez simplement des virgules dans la usingdéclaration et vous n'avez pas à le faire :)
Ry-
1
@minitech Cela ne fonctionne tout simplement pas ici - vous ne pouvez utiliser la virgule que lorsque les types sont égaux, pas pour les types inégaux. La façon de faire de Lukas est la manière canonique, l'IDE formate même cela différemment (notez l'absence d'indentation automatique de la seconde using).
Konrad Rudolph
8

L'exemple le plus pertinent auquel je puisse penser:

if(someCondition)
   if(someOtherCondition)
      DoSomething();
else
   DoSomethingElse();

Lequel ifsera elsejumelé? L'indentation implique que l'extérieur ifobtient le else, mais ce n'est pas vraiment la façon dont le compilateur le verra; l' intérieur if obtiendra le else, et l'extérieurif non. Vous devez savoir cela (ou le voir se comporter de cette façon en mode débogage) pour comprendre par inspection pourquoi ce code pourrait ne pas répondre à vos attentes. Cela devient plus déroutant si vous connaissez Python; dans ce cas, vous savez que l'indentation définit des blocs de code, vous vous attendez donc à ce qu'elle soit évaluée en fonction de l'indentation. C #, cependant, ne donne pas un flip volant sur les espaces blancs.

Cela dit, je ne suis pas particulièrement d'accord avec cette règle "toujours utiliser des crochets" à première vue. Cela rend le code très verticalement bruyant, ce qui réduit la possibilité de le lire rapidement. Si la déclaration est:

if(someCondition)
   DoSomething();

... alors il devrait être écrit comme ça. L'énoncé «toujours utiliser des parenthèses» sonne comme «toujours entourer les opérations mathématiques de parenthèses». Cela transformerait la déclaration très simple a * b + c / den ((a * b) + (c / d))introduisant la possibilité de manquer une parenthèse étroite (le fléau de nombreux codeurs), et pour quoi faire? L'ordre des opérations est bien connu et bien appliqué, les parenthèses sont donc redondantes. Vous n'utiliseriez que des parenthèses pour appliquer un ordre d'opérations différent de celui qui serait normalement appliqué: a * (b+c) / dpar exemple. Les accolades sont similaires; utilisez-les pour définir ce que vous voulez faire dans les cas où cela diffère de la valeur par défaut et n'est pas "évident" (subjectif, mais généralement assez logique).

KeithS
la source
3
@AlexBrown ... qui était exactement mon point. La règle énoncée dans le PO est "toujours utiliser des parenthèses, même pour des lignes simples", avec lesquelles je ne suis pas d'accord pour la raison que j'ai indiquée. Les crochets seraient aider le premier exemple de code, car le code ne se comportera pas la façon dont il est en retrait; vous devez utiliser des crochets pour associer elsele premier ifau lieu du second. Veuillez supprimer le downvote.
KeithS
5

En parcourant les réponses, personne n'a explicitement déclaré le genre de pratique dont j'ai l'habitude, en racontant l'histoire de votre code:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0)
    {
        j++;
    }
}

Devient:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0) j++;
}

Mettre le j++sur la même ligne que le si devrait signaler à quelqu'un d'autre: "Je veux seulement que ce bloc incrémente jamais j" . Bien sûr, cela ne vaut que si la ligne est aussi simpliste que possible, car mettre un point d'arrêt ici, comme le mentionne périodiquement , ne sera pas très utile.

En fait, je viens de parcourir une partie de l'API Twitter Storm qui a ce «type» de code en Java, voici l'extrait pertinent du code d'exécution, à la page 43 de ce diaporama :

...
Integer Count = counts.get(word);
if (Count=null) count=0;
count++
...

Le bloc de boucle for contient deux éléments, donc je ne voudrais pas intégrer ce code. C'est-à-dire jamais :

int j = 0;
for (int i = 0 ; i < 100 ; ++i) if (i % 2 == 0) j++;

C'est horrible et je ne sais même pas si cela fonctionne (comme prévu); ne fais pas ça . De nouvelles lignes et accolades aident à distinguer des morceaux de code séparés mais liés, de la même manière qu'une virgule ou un point-virgule le font en prose. Le bloc ci-dessus est aussi mauvais qu'une phrase vraiment longue avec quelques clauses et quelques autres instructions qui ne se cassent jamais ou ne s'arrêtent jamais pour distinguer des parties distinctes.

Si vous voulez vraiment télégraphier à quelqu'un d'autre, c'est un travail d'une seule ligne, utilisez un opérateur ou un ?:formulaire ternaire :

for (int i = 0 ; i < 100 ; ++i) (i%2 ? 0 : >0) j++;

Mais cela frôle le code-golf, et je pense que ce n'est pas une bonne pratique (ce n'est pas clair pour moi si je dois mettre le j ++ d'un côté :ou non). NB Je n'ai jamais exécuté d'opérateur ternaire en C ++ auparavant, je ne sais pas si cela fonctionne, mais il existe .

En bref:

Imaginez comment votre lecteur (c'est-à-dire la personne qui gère le code) interprète votre histoire (code). Soyez aussi clair que possible pour eux. Si vous savez que le codeur / étudiant novice maintient cela, peut-être même en laisser autant {}que possible, juste pour qu'ils ne se confondent pas.

Pureferret
la source
1
(1) Mettre la déclaration sur la même ligne la rend moins lisible, pas plus. Des pensées particulièrement simples comme un incrément sont facilement ignorées. Ne les mettre sur une nouvelle ligne. (2) Bien sûr, vous pouvez mettre votre forboucle sur une seule ligne, pourquoi cela ne devrait-il pas fonctionner? Cela fonctionne pour la même raison que vous pouvez omettre les accolades; newline n'est tout simplement pas significatif en C ++. (3) Votre exemple d'opérateur conditionnel, en plus d'être horrible, n'est pas valide en C ++.
Konrad Rudolph
@KonradRudolph merci, je suis un peu rouillé en C ++. Je n'ai jamais dit que (1) était plus lisible, mais cela signifierait que ce morceau de code était censé être en ligne sur une seule ligne. (2) Mon commentaire était plus que je ne serais pas en mesure de le lire et de savoir que cela fonctionnait, que ce soit du tout ou comme prévu; c'est un exemple de ce qu'il ne faut pas faire pour cette raison. (3) Merci, je n'ai pas écrit C ++ depuis longtemps. Je vais arranger ça maintenant.
Pureferret
2
En outre, mettre plus d'une expression sur une seule ligne rend le débogage du code plus difficile. Comment mettez-vous le point d'arrêt sur la 2ème expression de cette ligne?
Piotr Perak
5

Parce que lorsque vous avez deux déclarations sans {}, il est facile de rater un problème. Supposons que le code ressemble à ceci.

int error = 0;
enum hash_type hash = SHA256;
struct hash_value *hash_result = hash_allocate();

if ((err = prepare_hash(hash, &hash_result))) != 0)
    goto fail;
if ((err = hash_update(&hash_result, &client_random)) != 0)
    goto fail;
if ((err = hash_update(&hash_result, &server_random)) != 0)
    goto fail;
if ((err = hash_update(&hash_result, &exchange_params)) != 0)
    goto fail;
    goto fail;
if ((err = hash_finish(hash)) != 0)
    goto fail;

error = do_important_stuff_with(hash);

fail:
hash_free(hash);
return error;

Ça semble bien. Le problème est vraiment facile à manquer, surtout lorsque la fonction contenant le code est beaucoup plus grande. Le problème est qu'il goto failest exécuté sans condition. Vous pouvez facilement imaginer à quel point cela est frustrant (vous demandant pourquoi le dernier hash_updateéchoue toujours, après tout, tout semble bien hash_updatefonctionner).

Cependant, cela ne signifie pas que je suis à ajouter {}partout (à mon avis, voir {}partout est ennuyeux). Bien que cela puisse causer des problèmes, cela n'a jamais été le cas pour mes propres projets, car mon style de codage personnel interdit les conditions sans {}quand ils ne sont pas sur la même ligne (oui, je conviens que mon style de codage n'est pas conventionnel, mais j'aime ça, et je utiliser le style de code du projet lors de la contribution à d'autres projets). Cela rend le code suivant très bien.

if (something) goto fail;

Mais pas le suivant.

if (something)
    goto fail;
Konrad Borowski
la source
Exactement. Ne mettez pas le saut de ligne (complètement inutile) + indentation, et vous évitez complètement ce problème que tout le monde est toujours si rapide à soulever.
Alexander - Rétablir Monica
4

Il rend votre code plus lisible en définissant clairement la portée de vos boucles et blocs conditionnels. Cela vous évite également les erreurs accidentelles.

Drona
la source
4

wrt 6: C'est plus sûr car la suppression d'un pointeur nul est un no-op. Donc, s'il vous arrive de traverser accidentellement ce chemin deux fois, vous n'entraînerez pas de corruption de mémoire en libérant de la mémoire qui est libre ou a été allouée à autre chose.

Il s'agit principalement d'un problème avec les objets de portée de fichier statiques et les singletons qui n'ont pas une durée de vie très claire et sont connus pour être recréés après leur destruction.

Dans la plupart des cas, vous pouvez éviter cela en utilisant auto_ptrs

Tom Tanner
la source
6
S'il vous arrive de passer par ce chemin deux fois, vous avez une erreur de programmation. La définition d'un pointeur sur null pour rendre cette erreur moins dangereuse ne résout pas le problème sous-jacent.
Pete Becker
1
D'accord, mais j'ai déjà vu cela recommandé, et je crois que c'est dans certaines normes de programmation professionnelle. Je commentais davantage pourquoi le professeur de l'affiche l'avait proposé, plutôt que quand c'était bon
Tom Tanner
2
Suite à ce que Pete Becker a dit: cela ne résout pas le problème sous-jacent, mais cela peut le masquer. (Dans certains cas, vous devez définir un pointeur après l' NULLavoir supprimé. Si NULLla valeur du pointeur est correcte dans ces circonstances, par exemple, le pointeur pointe vers une valeur mise en cache et NULLindique un cache non valide. Mais lorsque vous voyez quelqu'un définir un pointeur vers NULLla dernière ligne d'un destructeur, vous vous demandez s'il connaît le C ++.)
James Kanze
4

J'aime la réponse acceptée de Luchian, en fait j'ai appris à la dure qu'il a raison, donc j'utilise toujours des accolades, même pour les blocs à une seule ligne. Cependant, personnellement, je fais une exception lors de l'écriture d'un filtre, comme vous le faites dans votre exemple. Ce:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0)
    {
        j++;
    }
}

me semble encombré. Il sépare la boucle for et l'instruction if en actions distinctes, alors qu'en réalité votre intention est une seule action: compter tous les entiers divisibles par 2. Dans un langage plus expressif, cela pourrait s'écrire quelque chose comme:

j = [1..100].filter(_%2 == 0).Count

Dans les langues qui manquent de fermetures, le filtre ne peut pas être exprimé dans une seule instruction, mais doit être une boucle for suivie d'une instruction if. Cependant, c'est toujours une action dans l'esprit du programmeur, et je pense que cela devrait se refléter dans le code, comme ceci:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
  if (i % 2 == 0)
{
    j++;
}
MikeFHay
la source
7
J'aime la façon dont tout le monde parvient à ignorer for (int i = 0; i < 100; i += 2);, dans le but de poursuivre l'argument de l'indentation ;-) Il y a probablement tout un combat séparé que nous pourrions avoir, comment "mieux" exprimer la logique "pour chacun idans une certaine plage avec une certaine propriété" en C ++ sans boucle, en utilisant une combinaison cauchemardesque d'algorithmes standard, filter_iteratoret / ou counting_iterator.
Steve Jessop
1
De plus, si nous avions cela, nous pourrions être en désaccord sur la façon de mettre en retrait la déclaration unique massive qui en résulte.
Steve Jessop
2
@Steve, ce n'est qu'un exemple. Il existe de nombreuses utilisations légitimes du modèle. Évidemment, si vous voulez compter les nombres de 1 à 100 qui sont divisibles par 2, tout ce que vous avez à faire est 100/2.
MikeFHay
2
Bien sûr, je sais, c'est pourquoi j'ai résumé "pour chacun idans une certaine plage avec une certaine propriété". C'est juste que généralement sur SO, les gens sont très rapides à ignorer la question réelle en faveur d'une approche complètement différente de l'exemple donné. Mais le retrait est important , donc nous ne le faisons pas ;-)
Steve Jessop
4

Une option pour aider à prévenir les erreurs décrites ci-dessus consiste à intégrer ce que vous souhaitez que vous n'ayez pas à utiliser d'accolades. Il est beaucoup plus difficile de ne pas remarquer les erreurs lorsque vous essayez de modifier le code.

if (condition) doSomething();
else doSomethingElse();

if (condition) doSomething();
    doSomething2(); // Looks pretty obviously wrong
else // doSomethingElse(); also looks pretty obviously wrong
Tigre noir
la source
5
La deuxième option produirait une erreur de compilation, car le elsen'est pas associé à un if.
Luchian Grigore
Un problème moins visible avec inline est que la plupart des IDE par défaut le changent en style indenté lors de l'utilisation de leur utilitaire de mise en forme automatique.
Honza Brabec
@Honza: c'est un problème politique très chargé. Si nous coopérons sur une base de code, soit nous devons utiliser le même style d'indentation dans les moindres détails, soit nous devons tous les deux accepter de ne pas mettre en forme automatiquement le code existant "juste parce que". Si l'ancien, le style convenu pourrait toujours inclure cela, mais vous devrez soit configurer votre IDE pour le respecter, soit ne pas utiliser le formatage automatique. Convenir que le format commun est "quel que soit mon format automatique IDE" est très bien si nous utilisons tous le même IDE pour toujours, pas si bon sinon.
Steve Jessop
3

Si vous êtes un compilateur, cela ne fait aucune différence. Les deux sont pareils.

Mais pour les programmeurs, le premier est plus clair, facile à lire et moins sujet aux erreurs.

PP
la source
2
À part ouvrir {sur sa propre ligne, de toute façon.
Rob
3

Un autre exemple de l'ajout d'accolades. Une fois, je cherchais un bug et j'ai trouvé un tel code:

void SomeSimpleEventHandler()
{
    SomeStatementAtTheBeginningNumber1;
    if (conditionX) SomeRegularStatement;
    SomeStatementAtTheBeginningNumber2;
    SomeStatementAtTheBeginningNumber3;
    if (!SomeConditionIsMet()) return;
    OtherwiseSomeAdditionalStatement1;
    OtherwiseSomeAdditionalStatement2;
    OtherwiseSomeAdditionalStatement3;
}

Si vous lisez la méthode ligne par ligne, vous remarquerez qu'il y a une condition dans la méthode qui retourne si elle n'est pas vraie. Mais en réalité, il ressemble à 100 autres gestionnaires d'événements simples qui définissent certaines variables en fonction de certaines conditions. Et un jour, le Fast Coder entre et ajoute une instruction de réglage de variable supplémentaire à la fin de la méthode:

{
    ...
    OtherwiseSomeAdditionalStatement3;
    SetAnotherVariableUnconditionnaly;
}

En conséquence, SetAnotherVariableUnconditionnaly est exécuté lorsque SomeConditionIsMet (), mais le gars rapide ne l'a pas remarqué parce que toutes les lignes sont presque similaires en taille et même lorsque la condition de retour est indentée verticalement, elle n'est pas si perceptible.

Si le retour conditionnel est formaté comme ceci:

if (!SomeConditionIsMet())
{
    return;
}

il est très visible et le Fast Coder le trouvera en un coup d'œil.

Artemix
la source
Si votre codeur rapide ne peut pas être dérangé pour repérer une returninstruction mise en évidence par la syntaxe dans le corps d'une fonction avant d'y ajouter quelque chose, vous ne devez pas laisser le codeur rapide s'approcher de votre code. Vous n'empêcherez pas un tel gars de traîner votre code en incluant des accolades.
cmaster - réintègre monica
@cmaster Il ne travaille plus avec nous. Quoi qu'il en soit, la mise en évidence de la syntaxe est bonne, mais n'oubliez pas qu'il y a des personnes qui ne voient pas clairement (j'ai même vu un article d'un programmeur aveugle l'année dernière).
Artemix
2

Je considère que le premier est clair, puis le second. Cela donne l'impression de fermer les instructions, avec peu de code, c'est bien quand le code devient complexe {...}aide beaucoup, même s'il est endifoubegin...end

//first
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0)
    {
        j++;
    }
}


//second
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
    if (i % 2 == 0)
        j++;
i++;
RollRoll
la source
1

Il est préférable de définir le pointeur sur NULL lorsque vous avez terminé avec lui.

Voici un exemple pourquoi:

La classe A fait ce qui suit:

  1. Alloue un bloc de mémoire
  2. Puis quelque temps plus tard, il supprime ce bloc de mémoire mais ne place pas le pointeur sur NULL

La classe B fait ce qui suit

  1. Alloue de la mémoire (et dans ce cas, il se trouve que le même bloc de mémoire a été supprimé par la classe A.)

À ce stade, la classe A et la classe B ont des pointeurs pointant vers le même bloc de mémoire, en ce qui concerne la classe A, ce bloc de mémoire n'existe pas car il en a fini avec lui.

Considérez le problème suivant:

Que se passe-t-il s'il y a une erreur logique dans la classe A qui a entraîné son écriture dans la mémoire qui appartient maintenant à la classe B?

Dans ce cas particulier, vous n'obtiendrez pas une erreur d'exception d'accès incorrecte car l'adresse mémoire est légale, tandis que la classe A endommage désormais efficacement les données de classe B.

La classe B peut éventuellement se bloquer si elle rencontre des valeurs inattendues et lorsqu'elle se bloque, il y a de fortes chances que vous passiez un temps assez long à chasser ce bogue dans la classe B lorsque le problème est dans la classe A.

Si vous aviez défini le pointeur de mémoire supprimé sur NULL, vous auriez obtenu une erreur d'exception dès qu'une erreur logique dans la classe A aurait tenté d'écrire sur le pointeur NULL.

Si vous vous inquiétez de l'erreur logique avec double suppression lorsque les pointeurs sont NULL pour la deuxième fois, ajoutez assert pour cela.

Aussi: Si vous allez voter contre, veuillez expliquer.

John
la source
2
S'il y avait une erreur logique, elle devrait être corrigée, plutôt que de la masquer.
uınbɐɥs
@Barmar, OP dit ... 6. Réglez le pointeur sur NULL après la suppression - Double protection contre la suppression // pas mal. Certaines personnes ont répondu de ne pas le définir sur Null et je dis pourquoi il devrait être défini sur NULL, quelle partie de 6. ma réponse au paramètre NULL ne tient pas dans 6?
John
1
@Shaquin, Et comment proposez-vous pour trouver ces erreurs logiques en premier lieu? Une fois que vous avez défini la variable de pointeur sur NULL après la suppression de la mémoire. Toute tentative de référence au pointeur NULL se bloquera sur le débogueur sur la ligne de votre tentative illégale. Vous pouvez remonter et voir où était l'erreur logique et résoudre le problème. Si vous ne définissez pas la variable de pointeur sur NULL après avoir supprimé la mémoire, votre tentative illégale d'écrire cette mémoire supprimée en raison d'erreurs logiques UNAWARE peut réussir et ne se bloque donc pas à ce stade. Ce n'est pas le masquer.
John
1

Toujours avoir des accolades est une règle très simple et robuste. Cependant, le code peut sembler inélégant lorsqu'il y a beaucoup d'accolades. Si les règles permettent d'omettre les accolades, il devrait y avoir des règles de style plus détaillées et des outils plus sophistiqués. Sinon, cela peut facilement résulter avec un code chaotique et déroutant (pas élégant). Par conséquent, la recherche d'une règle de style unique distincte du reste des guides de style et des outils utilisés est probablement infructueuse. Je vais juste apporter quelques détails importants sur cette règle # 3 qui n'ont même pas été mentionnés dans d'autres réponses.

Le premier détail intéressant est que la plupart des partisans de cette règle acceptent de la violer au cas où else. En d'autres termes, ils n'exigent pas de révision de ce code:

// pedantic rule #3
if ( command == Eat )
{
    eat();
}
else
{
    if ( command == Sleep )
    {
        sleep();
    }
    else
    {
        if ( command == Drink )
        {
            drink();
        }
        else
        {
            complain_about_unknown_command();
        }
    }
}

Au lieu de cela, s'ils le voient, ils peuvent même suggérer de l'écrire comme ça:

// not fully conforming to rule #3
if ( command == Eat )
{
    eat();
}
else if ( command == Sleep )
{
    sleep();
}
else if ( command == Drink )
{
    drink();
}
else
{
   complain_about_unknown_command();
}

C'est techniquement une violation de cette règle, car il n'y a pas de crochets entre elseet if. Une telle dualité des règles apparaît quand il faut essayer de l'appliquer automatiquement à la base de code avec un outil stupide. En effet, pourquoi argumenter, il suffit de laisser un outil pour appliquer automatiquement le style.

Le deuxième détail (qui est également souvent oublié par les partisans de cette règle) est que les erreurs qui peuvent se produire ne sont jamais uniquement dues à des violations de cette règle # 3. En fait, ceux-ci impliquent presque toujours des violations de la règle n ° 1 (que personne ne conteste). Toujours du point de vue des outils automatiques, il n'est pas difficile de créer un outil qui se plaint immédiatement lorsque la règle n ° 1 est violée et donc la plupart des erreurs peuvent être détectées en temps opportun.

Le troisième détail (qui est souvent oublié par les opposants à cette règle) est la nature confuse de la déclaration vide qui est représentée par un point-virgule unique. La plupart des développeurs ayant une certaine expérience sont devenus confus tôt ou tard par le seul point-virgule mal placé ou par une déclaration vide écrite à l'aide d'un seul point-virgule. Deux accolades bouclées au lieu d'un seul point-virgule sont visuellement beaucoup plus faciles à repérer.

Öö Tiib
la source
1

Je dois admettre que ce n'est pas toujours utilisé {}pour une seule ligne, mais c'est une bonne pratique.

  • Disons que vous écrivez un code sans crochets qui ressemble à ceci:

    for (int i = 0; i <100; ++ i) for (int j = 0; j <100; ++ j) DoSingleStuff ();

Et après un certain temps, vous voulez ajouter en jboucle d'autres choses et vous le faites simplement par alignement et oubliez d'ajouter des crochets.

  • La désallocation de mémoire est plus rapide. Disons que vous avez une grande portée et créez de grands tableaux à l'intérieur (sans newqu'ils soient en pile). Ces tableaux sont supprimés de la mémoire juste après avoir quitté la portée. Mais il est possible que vous utilisiez ce tableau en un seul endroit et il sera en pile pendant un certain temps et sera une sorte de déchet. Comme une pile a une taille limitée et assez petite, il est possible de dépasser la taille de la pile. Dans certains cas, il vaut donc mieux écrire {}pour éviter cela. REMARQUE ce n'est pas pour une seule ligne mais pour de telles situations:

    if (...) {// SomeStuff ... {// nous n'avons pas de if, while, etc. // SomeOtherStuff} // SomeMoreStuff}

  • La troisième façon de l'utiliser est similaire à la seconde. Il ne s'agit tout simplement pas de rendre la pile plus propre mais d' ouvrir certaines fonctions. Si vous utilisez mutexdes fonctions longues, il est généralement préférable de verrouiller et déverrouiller juste avant d'accéder aux données et juste après avoir fini de lire / écrire cela. REMARQUE: cette méthode est utilisée si vous en avez une classou structavec constructoret destructorpour verrouiller la mémoire.

  • De plus:

    if (...) if (...) SomeStuff (); else SomeOtherStuff (); // passe au second if, mais l'alligment montre que c'est le premier ...

Dans l'ensemble, je ne peux pas dire quelle est la meilleure façon de toujours l'utiliser {}pour une seule ligne, mais ce n'est rien de mal à faire.

MODIFICATION IMPORTANTE Si vous écrivez des crochets de code de compilation pour une seule ligne ne fait rien, mais si votre code sera interprété, il ralentit très très légèrement le code. Très légèrement.

ST3
la source
1

Il existe plusieurs façons d'écrire des instructions de contrôle; certaines combinaisons peuvent coexister sans nuire à la lisibilité, mais d'autres combinaisons causeront des problèmes. Le style

if (condition)
  statement;

coexistera confortablement avec certaines des autres façons d'écrire des déclarations de contrôle, mais pas aussi bien avec d'autres. Si les instructions contrôlées sur plusieurs lignes sont écrites comme suit:

if (condition)
{
  statement;
  statement;
}

alors il sera visuellement évident quelles ifinstructions contrôlent une seule ligne et lesquelles contrôlent plusieurs lignes. Si, cependant, les ifinstructions sur plusieurs lignes sont écrites comme suit:

if (condition) {
  statement;
  statement;
}

alors la probabilité que quelqu'un essaie d'étendre une ifconstruction à une seule instruction sans ajouter les accolades nécessaires peut être beaucoup plus élevée.

L'instruction single-statement-on-next line ifpeut également être problématique si la base de code utilise de manière significative le formulaire

if (condition) statement;

Ma préférence est que le fait d'avoir la déclaration sur sa propre ligne améliore généralement la lisibilité sauf dans les cas où il y a beaucoup de ifdéclarations avec des blocs de contrôle similaires, par exemple

if (x1 > xmax) x1 = xmax;
if (x1 < xmin) x1 = xmin;
if (x2 > xmax) x2 = xmax;
if (x2 < xmin) x2 = xmin;
etc.

dans ce cas, je précéderai et suivrai généralement ces groupes de ifdéclarations avec une ligne vierge pour les séparer visuellement des autres codes. Le fait d'avoir une gamme d'énoncés qui commencent tous par ifla même indentation fournira alors une indication visuelle claire qu'il y a quelque chose d'inhabituel.

supercat
la source