c++ - online - source code formatter astyle




Accolades inutiles en C++? (10)

En faisant une révision de code pour un collègue aujourd'hui, j'ai vu une chose particulière. Il avait entouré son nouveau code avec des accolades comme celle-ci:

Constructor::Constructor()
{
   existing code

   {
      New code: do some new fancy stuff here
   }

   existing code
}

Quel est le résultat, le cas échéant, de cela? Quelle pourrait être la raison de faire cela? D'où vient cette habitude?

Modifier:

Sur la base de la contribution et de quelques questions ci-dessous, je pense que je dois en ajouter à la question, même si j'ai déjà marqué une réponse.

L'environnement est des périphériques intégrés. Il y a beaucoup de code C hérité enveloppé dans des vêtements C ++. Il y a beaucoup de développeurs en C ++.

Il n'y a pas de sections critiques dans cette partie du code. Je l'ai seulement vu dans cette partie du code. Il n'y a pas d'allocation de mémoire majeure, seulement quelques drapeaux qui sont définis, et un peu de twittling.

Le code qui est entouré par des accolades est quelque chose comme:

{
   bool isInit;
   (void)isStillInInitMode(&isInit);
   if (isInit) {
     return isInit;
   }
}

(Ne vous dérange pas le code, il suffit de coller aux accolades ...;)) Après les accolades, il y a un peu plus de twittling, vérification d'état, et la signalisation de base.

J'ai parlé au gars et sa motivation était de limiter la portée des variables, nommer les conflits, et d'autres que je ne pouvais pas vraiment saisir.

De mon point de vue cela semble plutôt étrange et je ne pense pas que les accolades devraient être dans notre code. J'ai vu quelques bons exemples dans toutes les réponses sur pourquoi on pourrait entourer le code avec des accolades, mais ne devriez-vous pas séparer le code en méthodes à la place?


Alors, pourquoi utiliser des accolades "inutiles"?

  • À des fins de «délimitation» (comme mentionné ci-dessus)
  • Rendre le code plus lisible d'une certaine façon (à peu près comme utiliser #pragma , ou définir des "sections" qui peuvent être visualisées)
  • Parce que vous pouvez. Aussi simple que cela.

PS Ce n'est pas un code BAD; c'est 100% valide. Donc, c'est plutôt une question de goût (inhabituel).


Après avoir vu le code dans l'édition, je peux dire que les parenthèses inutiles sont probablement (dans la vue originale des codeurs) pour être 100% clair ce qui se passera pendant le si / alors, même si c'est seulement une ligne maintenant, il pourrait être plus de lignes plus tard, et les crochets garantissent que vous ne ferez pas d'erreur.

{
   bool isInit;
   (void)isStillInInitMode(&isInit);
   if (isInit) {
     return isInit;
   }
   return -1;
}

si ce qui précède était original, et supprimer "extras" entraînerait:

{
   bool isInit;
   (void)isStillInInitMode(&isInit);
   if (isInit) 
     return isInit;
   return -1;
}

alors, une modification ultérieure pourrait ressembler à ceci:

{
   bool isInit;
   (void)isStillInInitMode(&isInit);
   if (isInit) 
     CallSomethingNewHere();
     return isInit;
   return -1;
}

et cela, bien sûr, causerait un problème, puisque maintenant isInit serait toujours retourné, indépendamment du si / alors.


C'est parfois sympa car cela vous donne une nouvelle portée, où vous pouvez déclarer plus proprement de nouvelles variables (automatiques).

En C++ ce n'est peut-être pas si important puisque vous pouvez introduire de nouvelles variables n'importe où, mais peut-être que l'habitude vient de C , où vous ne pourriez pas le faire avant C99. :)

Puisque C++ a des destructeurs, il peut être pratique d'avoir des ressources (fichiers, mutex, etc.) automatiquement libérées au fur et à mesure de la fermeture de la portée, ce qui peut rendre les choses plus propres. Cela signifie que vous pouvez conserver une ressource partagée pendant une durée plus courte que si vous l'aviez saisie au début de la méthode.


Ceci est vraiment utile lors de l'utilisation de verrouillages avec des sections critiques dans la programmation multithread. Votre verrou de portée initialisé dans les accolades (généralement la première commande) sortira hors de la portée à la fin de la fin du bloc et ainsi d'autres threads pourront fonctionner à nouveau.


Je pense que d'autres ont déjà couvert la portée, donc je mentionnerai que les accolades inutiles pourraient également servir dans le processus de développement. Par exemple, supposons que vous travaillez sur une optimisation à une fonction existante. Basculer l'optimisation ou tracer un bogue vers une séquence particulière d'instructions est simple pour le programmeur - voir le commentaire avant les accolades:

// if (false) or if (0) 
{
   //experimental optimization  
}

Cette pratique est utile dans certains contextes tels que le débogage, les périphériques intégrés ou le code personnel.


Je suis d'accord avec "ruakh". Si vous voulez une bonne explication des différents niveaux de portée en C, consultez ce post:

Différents niveaux de portée dans l'application C

En général, l'utilisation de "Bloquer la portée" est utile si vous souhaitez simplement utiliser une variable temporaire dont vous n'avez pas besoin de suivre la durée de vie de l'appel de fonction. En outre, certaines personnes l'utilisent pour que vous puissiez utiliser le même nom de variable dans plusieurs emplacements pour plus de commodité, même si ce n'est généralement pas une bonne idée. par exemple:

int unusedInt = 1;

int main(void) {
  int k;

  for(k = 0; k<10; k++) {
    int returnValue = myFunction(k);
    printf("returnValue (int) is: %d (k=%d)",returnValue,k);
  }

  for(k = 0; k<100; k++) {
    char returnValue = myCharacterFunction(k);
    printf("returnValue (char) is: %c  (k=%d)",returnValue,k);
  }

  return 0;
}

Dans cet exemple particulier, j'ai défini deux fois returnValue, mais comme il est juste à la portée du bloc, à la place de la portée de la fonction (ie, la portée de la fonction serait, par exemple, déclarer returnValue juste après int main (void)). obtenir des erreurs de compilation, car chaque bloc est inconscient de l'instance temporaire de returnValue déclarée.

Je ne peux pas dire que c'est une bonne idée en général (ie: vous ne devriez probablement pas réutiliser les noms de variables de façon répétée de bloc à bloc), mais en général, cela fait gagner du temps et vous évite d'avoir à gérer le valeur de returnValue sur l'ensemble de la fonction.

Enfin, veuillez noter la portée des variables utilisées dans mon exemple de code:

int:  unusedInt:   File and global scope (if this were a static int, it would only be file scope)
int:  k:           Function scope
int:  returnValue: Block scope
char: returnValue: Block scope

Les objets sont automatiquement détruits quand ils sortent de la portée ...


Tout le monde a déjà couvert correctement les possibilités de portée, RAII etc., mais puisque vous mentionnez un environnement intégré, il y a une autre raison potentielle:

Peut-être que le développeur ne fait pas confiance à l'allocation de registre de ce compilateur ou veut contrôler explicitement la taille de la trame de la pile en limitant le nombre de variables automatiques dans la portée à la fois.

Voici isInit sera probablement sur la pile:

{
   bool isInit;
   (void)isStillInInitMode(&isInit);
   if (isInit) {
     return isInit;
   }
}

Si vous isInit les accolades, l'espace pour isInit peut être réservé dans le cadre de la pile même après qu'il pourrait potentiellement être réutilisé: s'il y a beaucoup de variables automatiques avec une portée localisée similaire, et que la taille de votre pile est limitée .

De même, si votre variable est allouée à un registre, sortir de la portée devrait fournir une indication forte que le registre est maintenant disponible pour la réutilisation. Vous devriez regarder l'assembleur généré avec et sans les accolades pour déterminer si cela fait une réelle différence (et le profil - ou regarder le débordement de la pile - pour voir si cette différence compte vraiment).


Un objectif possible est de contrôler la portée variable . Et puisque les variables avec stockage automatique sont détruites lorsqu'elles sont hors de portée, cela peut également permettre à un destructeur d'être appelé plus tôt qu'il ne le ferait autrement.


Une raison pourrait être que la durée de vie des variables déclarées dans le nouveau bloc accolades est limitée à ce bloc. Une autre raison qui vient à l'esprit est de pouvoir utiliser le pliage de code dans l'éditeur favori.





code-formatting