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 ceci:
Constructor::Constructor()
{
existing code
{
New code: do some new fancy stuff here
}
existing code
}
Quel en est le résultat, le cas échéant? Quelle pourrait en être la raison? D'où vient cette habitude?
Éditer:
Sur la base des commentaires 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 constitué de périphériques embarqués. Il y a beaucoup de code C hérité enveloppé dans des vêtements C ++. Il y a beaucoup de développeurs C devenus C ++.
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'allocations de mémoire majeures, juste quelques indicateurs qui sont définis et quelques petits changements.
Le code entouré d'accolades est quelque chose comme:
{
bool isInit;
(void)isStillInInitMode(&isInit);
if (isInit) {
return isInit;
}
}
(Ne vous inquiétez pas du code, tenez-vous-en 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é au gars et sa motivation était de limiter la portée des variables, de 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 de bons exemples dans toutes les réponses sur les raisons pour lesquelles on pouvait entourer le code avec des accolades, mais ne devriez-vous pas plutôt séparer le code en méthodes?
la source
Réponses:
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 peut-être que l'habitude vientC
, où vous ne pourriez pas faire cela avant C99. :)Depuis qu'il
C++
a des destructeurs, il peut également être pratique de libérer automatiquement des ressources (fichiers, mutex, etc.) à la sortie 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 récupérée au début de la méthode.la source
Un objectif possible est de contrôler la portée des variables . Et comme 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.
la source
for
déclarations pour créer unint i;
C89 de courte durée . Vous ne suggérez sûrement pas que chacunfor
devrait être dans une fonction distincte?Les accolades supplémentaires sont utilisées pour définir la portée de la variable déclarée à l'intérieur des accolades. Cela est fait pour que le destructeur soit appelé lorsque la variable sort de la 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:
Comme vous pouvez le voir, de cette manière, vous pouvez utiliser
scoped_lock
dans une fonction et en même temps définir sa portée en utilisant des accolades supplémentaires. Cela garantit que même si le code en dehors des accolades supplémentaires peut être exécuté par plusieurs threads simultanément, le code à l'intérieur des accolades sera exécuté par exactement un thread à la fois.la source
scoped_lock
qui sera détruit sur les exceptions. Je préfère généralement introduire une nouvelle portée pour le verrou, mais dans certains cas,unlock
c'est très utile. Par exemple, pour déclarer une nouvelle variable locale dans la section critique et l'utiliser plus tard. (Je sais que je suis en retard, mais juste pour être complet ...)Comme d'autres l'ont souligné, un nouveau bloc introduit une nouvelle portée, permettant d'écrire un peu de code avec ses propres variables qui ne détruisent pas l'espace de noms du code environnant et n'utilisent pas les ressources plus longtemps que nécessaire.
Cependant, il y a une autre bonne raison de faire cela.
Il s'agit simplement d'isoler un bloc de code qui atteint un (sous) objectif particulier. Il est rare qu'une seule instruction produise un effet de calcul que je souhaite; il en faut généralement plusieurs. Les placer dans un bloc (avec un commentaire) me permet de dire au lecteur (souvent moi-même à une date ultérieure):
par exemple
Vous pourriez dire que je devrais écrire une fonction pour faire tout cela. Si je ne le fais qu'une seule fois, l'écriture d'une fonction ajoute simplement une syntaxe et des paramètres supplémentaires; il semble peu utile. Pensez simplement à cela 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 masquer le bloc.
Je le fais tout le temps. C'est un grand plaisir de connaître les limites du code que je dois inspecter, et encore mieux de savoir que si ce morceau n'est pas celui que je veux, je n'ai à regarder aucune des lignes.
la source
Une raison pourrait être que la durée de vie de toutes les 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.
la source
C'est la même chose qu'un bloc
if
(ouwhile
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:
using
.Exemple 1:
S'il
my_variable
s'avère que c'est un nom particulièrement bon pour deux variables différentes qui sont utilisées isolément l'une de l'autre, alors la portée explicite vous permet d'éviter d'inventer un nouveau nom juste pour éviter le conflit de nom.Cela vous permet également d'éviter d'utiliser
my_variable
hors de sa portée par accident.Exemple 2:
Les situations pratiques où cela est utile sont rares et peuvent indiquer que le code est mûr pour la refactorisation, mais le mécanisme est là si vous en avez vraiment besoin.
Exemple 3:
Cela peut être important pour RAII dans les cas où la nécessité de libérer des ressources ne «tombe» pas naturellement sur les limites des fonctions ou des structures de contrôle.
la source
Ceci est vraiment utile lorsque vous utilisez des verrous étendus en conjonction 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 de la portée à la fin de la fin du bloc et ainsi les autres threads pourront s'exécuter à nouveau.
la source
Tous les autres ont déjà couvert correctement les possibilités de portée, RAII, etc., mais comme vous mentionnez un environnement embarqué, 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 souhaite 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
probablement sur la pile:Si vous supprimez 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, cela pourrait être un problème.De même, si votre variable est allouée à un registre, sortir de la portée devrait fournir un indice fort que le registre est maintenant disponible pour une réutilisation. Vous devriez regarder l'assembleur généré avec et sans les accolades pour déterminer si cela fait une vraie différence (et le profiler - ou surveiller le débordement de pile - pour voir si cette différence compte vraiment).
la source
Je pense que d'autres ont déjà couvert la portée, je vais donc mentionner que les accolades inutiles pourraient également servir dans 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 vers une séquence particulière d'instructions est simple pour le programmeur - voir le commentaire avant les accolades:
Cette pratique est utile dans certains contextes tels que le débogage, les périphériques intégrés ou le code personnel.
la source
Je suis d'accord avec "ruakh". Si vous voulez une bonne explication des différents niveaux de portée en C, consultez cet article:
Différents niveaux de portée dans l'application C
En général, l'utilisation de "Block scope" est utile si vous voulez simplement utiliser une variable temporaire dont vous n'avez pas besoin de garder une trace pendant la durée de vie de l'appel de fonction. De plus, certaines personnes l'utilisent pour que vous puissiez utiliser le même nom de variable à plusieurs endroits pour plus de commodité, bien que ce ne soit généralement pas une bonne idée. par exemple:
Dans cet exemple particulier, j'ai défini returnValue deux fois, mais comme il s'agit simplement de la portée du bloc, au lieu de la portée de la 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 obtenir toutes les erreurs du compilateur, 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 (c'est-à-dire: vous ne devriez probablement pas réutiliser les noms de variables à plusieurs reprises d'un bloc à l'autre), mais en général, cela fait gagner du temps et vous évite d'avoir à gérer le valeur de returnValue dans toute la fonction.
Enfin, veuillez noter la portée des variables utilisées dans mon exemple de code:
la source
Alors, pourquoi utiliser des accolades "inutiles"?
#pragma
ou définir des «sections» qui peuvent être visualisées)PS Ce n'est pas un mauvais code; c'est 100% valide. Donc, c'est plutôt une question de goût (rare).
la source
Après avoir vu le code dans l'édition, je peux dire que les crochets inutiles sont probablement (dans la vue des codeurs d'origine) pour être 100% clair ce qui se passera pendant le si / alors, même s'il ne s'agit que d'une ligne maintenant, cela pourrait être plus de lignes plus tard, et les crochets garantissent que vous ne ferez pas d'erreur.
si ce qui précède était original, et la suppression des "extras" entraînera:
alors, une modification ultérieure pourrait ressembler à ceci:
et cela, bien sûr, causerait un problème, puisque maintenant isInit serait toujours retourné, quel que soit le if / then.
la source
Les objets sont automatiquement détruits lorsqu'ils sont hors de portée ...
la source
Les classes liées à l'interface utilisateur, en particulier Qt, sont un autre exemple d'utilisation.
Par exemple, vous avez une interface utilisateur et beaucoup de widgets, chacun d'entre eux a obtenu son propre espace, mise en page et etc. Au lieu de les nommer compliquées ,
space1, space2, spaceBetween, layout1, ...
vous pouvez vous épargner des noms non-descriptives pour les variables qui existent seulement dans deux-trois lignes de code.Eh bien, certains pourraient dire que vous devriez le diviser en méthodes, mais créer 40 méthodes non réutilisables ne semble pas correct - j'ai donc décidé de simplement ajouter des accolades et des commentaires avant eux, donc cela ressemble à un bloc logique. Exemple:
Je ne peux pas dire que ce soit la meilleure pratique, mais c'est une bonne pratique pour le code hérité.
J'ai eu ces problèmes lorsque beaucoup de gens ont ajouté leurs propres composants à l'interface utilisateur et que certaines méthodes sont devenues vraiment massives, mais il n'est pas pratique de créer 40 méthodes d'utilisation unique à l'intérieur d'une classe qui a déjà gâché.
la source