web-dev-qa-db-fra.com

Des accolades inutiles en C ++?

Aujourd'hui, lors de l'examen d'un code pour un collègue, j'ai constaté une chose particulière. Il avait entouré son nouveau code avec des accolades telles que:

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?

Edit:

Sur la base des commentaires et des questions ci-dessous, j’estime que je dois en ajouter, 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++ tournés.

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

Le code qui est entouré d'accolades ressemble à ceci:

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

(Ne vous occupez pas du code, contentez-vous de vous limiter aux accolades ...;)) Après les accolades, il y a un peu plus de twiddling, de vérification d'état et de signalisation de base.

J'ai parlé à ce type et sa motivation était de limiter la portée des variables, nommant des affrontements, et quelques autres que je ne pouvais pas vraiment comprendre.

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 pouvait entourer le code avec des accolades, mais ne devriez-vous pas séparer le code en méthodes?

173
iikkoo

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

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

Puisque C++ possède des destructeurs, il peut également s'avérer utile de disposer de ressources (fichiers, mutex, etc.) libérées automatiquement à la sortie de la portée, ce qui rend les choses plus propres. Cela signifie que vous pouvez conserver certaines ressources partagées pendant une durée plus courte que si vous l'aviez saisie au début de la méthode.

266
unwind

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

167
ruakh

Les accolades supplémentaires permettent de définir la portée de la variable déclarée à l'intérieur des accolades. Il est fait en sorte que le destructeur sera appelé lorsque la variable sortira de sa portée. Dans le destructeur, vous pouvez libérer un mutex (ou toute autre ressource) afin que d’autres puissent l’acquérir.

Dans mon code de production, j'ai écrit quelque chose comme ceci:

void f()
{
   //some code - MULTIPLE threads can execute this code at the same time

   {
       scoped_lock lock(mutex); //critical section starts here

       //critical section code
       //EXACTLY ONE thread can execute this code at a time

   } //mutex is automatically released here

  //other code  - MULTIPLE threads can execute this code at the same time
}

Comme vous pouvez le voir, de cette manière, vous pouvez utiliser scoped_lock dans une fonction et en même temps, vous pouvez définir sa portée en utilisant des accolades supplémentaires. Cela garantit que même si le code situé en dehors des accolades supplémentaires peut être exécuté simultanément par plusieurs threads, le code situé entre les accolades sera exécuté par exactement un thread à la fois.

100
Nawaz

Comme d'autres l'ont souligné, un nouveau bloc introduit une nouvelle portée, lui permettant d'écrire un morceau de code avec ses propres variables qui ne détruisent pas l'espace de noms du code environnant et n'utilisent pas de ressources plus longtemps que nécessaire.

Cependant, il existe une autre bonne raison de le faire.

Il s'agit simplement d'isoler un bloc de code permettant d'atteindre un (sous) objectif particulier. Il est rare qu’une seule déclaration produise un effet de calcul que je veux; habituellement, il en faut plusieurs. Placer ceux-ci dans un bloc (avec un commentaire) me permet de dire au lecteur (souvent moi-même à une date ultérieure):

  • Ce morceau a un but conceptuel cohérent
  • Voici tout le code nécessaire
  • Et voici un commentaire sur le morceau.

par exemple.

{  // update the moving average
   i= (i+1) mod ARRAYSIZE;
   sum = sum - A[i];
   A[i] = new_value;
   sum = sum + new_value;
   average = sum / ARRAYSIZE ;  
}

Vous pourriez argumenter que je devrais écrire une fonction pour faire tout ça. Si je ne le fais qu'une fois, écrire une fonction ne fait qu'ajouter de la syntaxe et des paramètres supplémentaires; il semble y avoir peu d'intérêt. Pensez-y simplement comme une fonction anonyme et sans paramètre.

Si vous avez de la chance, votre éditeur aura une fonction de pliage/dépliage qui vous permettra même de cacher le bloc.

Je fais ça tout le temps. C'est un grand plaisir de connaître les limites du code que j'ai besoin d'inspecter, et encore mieux de savoir que si cette partie n'est pas celle que je veux, je n'ai pas à regarder aucune des lignes.

49
Ira Baxter

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

23
arne

Ceci est identique à un bloc if (ou while etc ..), juste sansif. En d'autres termes, vous introduisez une étendue sans introduire de structure de contrôle.

Cette "portée explicite" est généralement utile dans les cas suivants:

  1. Pour éviter les conflits de noms.
  2. Pour atteindre using.
  3. Pour contrôler quand les destructeurs sont appelés.

Exemple 1:

{
    auto my_variable = ... ;
    // ...
}

// ...

{
    auto my_variable = ... ;
    // ...
}

Si my_variable se trouve être un très bon nom pour deux variables différentes utilisées isolément. La portée explicite vous permet d’éviter d’inventer un nouveau nom simplement pour éviter les conflits de noms.

Cela vous permet également d'éviter d'utiliser my_variable hors de son champ d’application par accident.

Exemple 2:

namespace N1 { class A { }; }
namespace N2 { class A { }; }

void foo() {

    {
        using namespace N1;
        A a; // N1::A.
        // ...
    }

    {
        using namespace N2;
        A a; // N2::A.
        // ...
    }

}

Les situations pratiques, lorsque cela est utile, sont rares et peuvent indiquer que le code est mûr pour le refactoring, mais le mécanisme existe si vous en aviez réellement besoin.

Exemple 3:

{
    MyRaiiClass guard1 = ...;

    // ...

    {
        MyRaiiClass guard2 = ...;
        // ...
    } // ~MyRaiiClass for guard2 called.

    // ...

} // ~MyRaiiClass for guard1 called.

Cela peut être important pour RAII dans les cas où la nécessité de libérer des ressources ne "tombe" pas naturellement sur les frontières des fonctions ou des structures de contrôle.

16

Ceci est très utile lorsque vous utilisez des verrous sectorisés avec des sections critiques dans la programmation multithread. Votre verrou scoped initialisé entre les accolades (généralement la première commande) disparaîtra de la portée à la fin de la fin du bloc et les autres threads pourront alors s'exécuter à nouveau.

14
learnvst

Tous les autres ont déjà couvert correctement les possibilités de portée, de RAII, etc., mais puisque vous parlez d'un environnement embarqué, il existe 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 du cadre de la pile en limitant le nombre de variables automatiques dans la portée à la fois.

Ici isInit sera probablement sur la pile:

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

Si vous supprimez les accolades, de l’espace pour isInit peut être réservé dans le cadre de la pile, même après sa réutilisation potentielle: s’il existe de nombreuses variables automatiques ayant une étendue localisée similaire, et que la taille de votre pile est limitée, Cela pourrait être un problème.

De même, si votre variable est affectée à un registre, sortir du champ d'application devrait fournir un indice fort que le registre est maintenant disponible pour une réutilisation. Vous devez examiner l'assembleur généré avec et sans les accolades pour déterminer si cela fait une réelle différence (et le profiler - ou surveiller le dépassement de capacité de la pile - pour voir si cette différence compte vraiment).

12
Useless

Je pense que d’autres ont déjà couvert la portée, alors je mentionnerai que des accolades inutiles pourraient également servir à faire avancer le processus de développement. Par exemple, supposons que vous travaillez sur une optimisation d'une fonction existante. Basculer l'optimisation ou tracer un bogue dans une séquence d'instructions particulière est simple pour le programmeur - voir le commentaire précédant 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.

11
kertronux

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 "Block scope" est utile si vous souhaitez simplement utiliser une variable temporaire que vous n'avez pas à suivre pendant toute la durée de l'appel de la fonction. De plus, certaines personnes l'utilisent pour que vous puissiez utiliser le même nom de variable dans plusieurs emplacements pour plus de commodité, bien que ce ne soit 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 returnValue deux fois, mais comme il s'agit simplement de portée de bloc, et non de portée de fonction (c'est-à-dire: la portée de la fonction serait, par exemple, de déclarer returnValue juste après int main (void)), je ne le fais pas. récupère toutes les erreurs du compilateur, car chaque bloc n'a aucune idée 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 (c'est-à-dire: vous ne devriez probablement pas réutiliser les noms de variables de manière répétée d'un bloc à l'autre), mais en général, cela vous fait gagner du temps et vous évite d'avoir à gérer les valeur de returnValue sur toute 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
10
DevNull

Alors, pourquoi utiliser des accolades "inutiles"?

  • Aux fins de "cadrage" (comme mentionné ci-dessus)
  • Rendre le code plus lisible d'une certaine manière (un peu comme utiliser #pragma, ou définissant des "sections" pouvant être visualisées)
  • Parce que vous pouvez. Aussi simple que cela.

P.S. Ce n'est pas mauvais code; c'est 100% valide. Donc, c'est plutôt une question de goût (peu commun).

5
Dr.Kameleon

Après avoir visualisé le code dans l'édition, je peux dire que les crochets inutiles sont probablement (dans la vue des codeurs d'origine) parfaitement clairs, ce qui se passera pendant le if/then, même s'il ne s'agit que d'une ligne, cela pourrait être plus de lignes plus tard, et les crochets vous 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 en supprimant les "extras", on obtiendrait:

{
   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, poserait un problème, car isInit serait toujours renvoyé, quel que soit le if/then.

5
nycynik

Les objets sont détruits automatiquement lorsqu'ils sortent de leur champ de vision ...

4
user677656

Les classes liées à l'interface utilisateur, en particulier Qt, sont un autre exemple d'utilisation.

Par exemple, vous avez une interface utilisateur compliquée et de nombreux widgets, chacun d'entre eux ayant son propre espacement, sa propre présentation, etc. Au lieu de les nommer space1, space2, spaceBetween, layout1, ... vous pouvez vous épargner des noms non descriptifs pour des variables qui n'existent que dans deux ou trois lignes de code.

Certains diront peut-être que vous devriez scinder le document en méthodes, mais la création de 40 méthodes non réutilisables n’a pas l’air de plaire. J’ai donc décidé d’ajouter des accolades et des commentaires devant eux pour que cela ressemble à un bloc logique. Exemple:

// Start video button 
{ 
   <here the code goes> 
}
// Stop video button
{
   <...>
}
// Status label
{
   <...>
}

Je ne peux pas dire que ce soit la meilleure pratique, mais c'est une bonne solution pour le code hérité.

Vous avez ces problèmes lorsque beaucoup de gens ont ajouté leurs propres composants à l'interface utilisateur et que certaines méthodes sont devenues vraiment énormes, mais il n'est pas pratique de créer 40 méthodes d'utilisation ponctuelle dans la classe qui ont déjà été gâchées.

1
htzfun