Ao fazer uma revisão de código para um colega hoje, vi uma coisa peculiar. Ele havia cercado seu novo código com chaves assim:
Constructor::Constructor()
{
existing code
{
New code: do some new fancy stuff here
}
existing code
}
Qual é o resultado disso, se houver? Qual poderia ser o motivo para fazer isso? De onde vem esse hábito?
Editar:
Com base na entrada e em algumas perguntas abaixo, sinto que tenho que adicionar algumas à pergunta, mesmo que já tenha marcado uma resposta.
O ambiente é dispositivos incorporados. Há muito código C herdado envolto em roupas C ++. Muitos desenvolvedores de C ++ se tornaram C ++.
Não há seções críticas nesta parte do código. Eu só vi isso nesta parte do código. Não há grandes alocações de memória, apenas alguns sinalizadores configurados e alguns ajustes.
O código cercado por chaves é algo como:
{
bool isInit;
(void)isStillInInitMode(&isInit);
if (isInit) {
return isInit;
}
}
(Não se importe com o código, fique com os chavetas ...;)) Após os chavetas, há mais alguns ajustes, verificação de estado e sinalização básica.
Conversei com o cara e sua motivação era limitar o escopo de variáveis, conflitos de nomes e algumas outras que eu realmente não conseguia entender.
Do meu ponto de vista, isso parece um pouco estranho e eu não acho que o aparelho esteja no nosso código. Vi alguns bons exemplos em todas as respostas sobre por que alguém poderia colocar código entre chaves, mas você não deveria separar o código em métodos?
fonte
Respostas:
Às vezes, é bom, pois fornece um novo escopo, onde você pode "limpar" de forma mais limpa novas variáveis (automáticas).
Em
C++
isto não é talvez tão importante, pois você pode introduzir novas variáveis em qualquer lugar, mas talvez o hábito é deC
, onde você não poderia fazer isso até C99. :)Como
C++
possui destruidores, também pode ser útil ter recursos (arquivos, mutexes, qualquer que seja) liberados automaticamente à medida que o escopo termina, o que pode tornar as coisas mais limpas. Isso significa que você pode se apegar a algum recurso compartilhado por um período mais curto do que faria se o pegasse no início do método.fonte
Um objetivo possível é controlar o escopo variável . E como as variáveis com armazenamento automático são destruídas quando ficam fora do escopo, isso também pode permitir que um destruidor seja chamado mais cedo do que seria.
fonte
for
declarações para criar uma vida curtaint i;
no C89. Certamente você não está sugerindo que todosfor
deveriam estar em uma função separada?As chaves extras são usadas para definir o escopo da variável declarada dentro das chaves. Isso é feito para que o destruidor seja chamado quando a variável sair do escopo. No destruidor, você pode liberar um mutex (ou qualquer outro recurso) para que outros possam adquiri-lo.
No meu código de produção, escrevi algo como isto:
Como você pode ver, dessa maneira, você pode usar
scoped_lock
em uma função e, ao mesmo tempo, definir seu escopo usando chaves extras. Isso garante que, mesmo que o código fora dos chavetas extras possa ser executado por vários threads simultaneamente, o código dentro dos chavetas será executado exatamente por um thread por vez.fonte
scoped_lock
que será destruído em exceções. Normalmente, prefiro introduzir um novo escopo também para o bloqueio, mas em alguns casos issounlock
é muito útil. Por exemplo, para declarar uma nova variável local na seção crítica e usá-la mais tarde. (Eu sei que estou atrasado, mas apenas para ser completo ...)Como outros já apontaram, um novo bloco introduz um novo escopo, permitindo escrever um pouco de código com suas próprias variáveis que não descartam o espaço de nomes do código circundante e não usam recursos mais do que o necessário.
No entanto, há outra boa razão para fazer isso.
É simplesmente isolar um bloco de código que atinja um (sub) propósito específico. É raro que uma única declaração atinja o efeito computacional que eu quero; geralmente leva vários. Colocar aqueles em um bloco (com um comentário) me permite dizer ao leitor (geralmente eu mesmo em uma data posterior):
por exemplo
Você pode argumentar que eu deveria escrever uma função para fazer tudo isso. Se eu fizer apenas uma vez, escrever uma função adiciona sintaxe e parâmetros adicionais; parece pouco sentido. Pense nisso como uma função anônima e sem parâmetros.
Se você tiver sorte, seu editor terá uma função de dobra / desdobramento que permitirá que você oculte o bloco.
Eu faço isso toda hora. É um grande prazer conhecer os limites do código que preciso inspecionar e ainda melhor saber que, se esse pedaço não for o que eu quero, não preciso olhar para nenhuma das linhas.
fonte
Uma razão pode ser que o tempo de vida de qualquer variável declarada dentro do novo bloco de chaves seja restrito a esse bloco. Outro motivo que vem à mente é poder usar a dobragem de código no editor favorito.
fonte
É o mesmo que um bloco
if
(ouwhile
etc.), apenas semif
. Em outras palavras, você introduz um escopo sem introduzir uma estrutura de controle.Esse "escopo explícito" geralmente é útil nos seguintes casos:
using
.Exemplo 1:
Se por
my_variable
acaso for um nome particularmente bom para duas variáveis diferentes usadas isoladamente uma da outra, o escopo explícito permitirá evitar inventar um novo nome apenas para evitar o conflito de nomes.Isso também permite evitar o uso
my_variable
fora do escopo pretendido por acidente.Exemplo 2:
Situações práticas em que isso é útil são raras e podem indicar que o código está pronto para refatoração, mas o mecanismo está presente caso você realmente precise dele.
Exemplo 3:
Isso pode ser importante para a RAII nos casos em que a necessidade de liberar recursos naturalmente não "cai" nos limites das funções ou estruturas de controle.
fonte
Isso é realmente útil ao usar bloqueios de escopo em conjunto com seções críticas na programação multithread. Seu bloqueio de escopo inicializado entre chaves (geralmente o primeiro comando) ficará fora do escopo no final do final do bloco e, portanto, outros threads poderão executar novamente.
fonte
Todo mundo já cobriu corretamente as possibilidades de escopo, RAII etc., mas como você menciona um ambiente incorporado, há um outro motivo potencial:
Talvez o desenvolvedor não confie na alocação de registro deste compilador ou queira controlar explicitamente o tamanho do quadro da pilha limitando o número de variáveis automáticas no escopo de uma só vez.
Aqui
isInit
provavelmente estará na pilha:Se você remover os chavetas, o espaço para
isInit
poderá ser reservado no quadro da pilha, mesmo depois que possa ser reutilizado: se houver muitas variáveis automáticas com escopo localizado da mesma forma e o tamanho da pilha for limitado, isso pode ser um problema.Da mesma forma, se sua variável for alocada para um registro, sair do escopo deve fornecer uma forte dica de que o registro agora está disponível para reutilização. Você teria que olhar para o assembler gerado com e sem chaves para descobrir se isso faz alguma diferença real (e criar um perfil - ou observar o estouro da pilha - para ver se essa diferença realmente importa).
fonte
Acho que outros já abordaram o escopo, portanto, mencionarei que os aparelhos desnecessários também podem servir a um propósito no processo de desenvolvimento. Por exemplo, suponha que você esteja trabalhando em uma otimização para uma função existente. Alternar a otimização ou rastrear um bug para uma sequência específica de instruções é simples para o programador - veja o comentário antes das chaves:
Essa prática é útil em certos contextos, como depuração, dispositivos incorporados ou código pessoal.
fonte
Eu concordo com "ruakh". Se você deseja uma boa explicação dos vários níveis de escopo em C, confira este post:
Vários níveis de escopo na aplicação C
Em geral, o uso do "Escopo do bloco" é útil se você deseja usar apenas uma variável temporária que não precisa acompanhar durante toda a vida útil da chamada de função. Além disso, algumas pessoas o usam para que você possa usar o mesmo nome de variável em vários locais por conveniência, embora isso geralmente não seja uma boa ideia. por exemplo:
Neste exemplo em particular, eu defini returnValue duas vezes, mas como está apenas no escopo do bloco, em vez do escopo da função (ou seja: o escopo da função seria, por exemplo, declarar returnValue logo após int main (void)), não obtenha qualquer erro do compilador, pois cada bloco está alheio à instância temporária de returnValue declarada.
Não posso dizer que essa seja uma boa ideia em geral (ou seja: você provavelmente não deve reutilizar nomes de variáveis repetidamente de bloco a bloco), mas, em geral, economiza tempo e evita a necessidade de gerenciar o valor de returnValue em toda a função.
Por fim, observe o escopo das variáveis usadas no meu exemplo de código:
fonte
Então, por que usar chaves "desnecessárias"?
#pragma
ou definir "seções" que podem ser visualizadas)PS Não é um código MAU; é 100% válido. Portanto, é uma questão de gosto (incomum).
fonte
Depois de visualizar o código na edição, posso dizer que os colchetes desnecessários provavelmente são (na visão dos codificadores originais) 100% claros do que acontecerá durante o if / then, mesmo que seja apenas uma linha agora, pode ser mais linhas depois, e os colchetes garantem que você não cometa um erro.
se o acima foi original, e a remoção de "extras" resultará em:
então, uma modificação posterior pode ser assim:
e isso, é claro, causaria um problema, já que agora isInit sempre seria retornado, independentemente do if / then.
fonte
Os objetos são destruídos automaticamente quando ficam fora do escopo ...
fonte
Outro exemplo de uso são as classes relacionadas à interface do usuário, especialmente Qt.
Por exemplo, você tem uma interface do usuário complicada e muitos widgets, cada um com seu próprio espaçamento, layout e etc. Em vez de nomeá-los,
space1, space2, spaceBetween, layout1, ...
você pode se salvar de nomes não descritivos para variáveis que existem apenas em duas ou três linhas de código.Bem, alguns podem dizer que você deve dividi-lo em métodos, mas a criação de 40 métodos não reutilizáveis não parece ok - então eu decidi apenas adicionar chaves e comentários antes deles, para que pareça um bloco lógico. Exemplo:
Não posso dizer que é a melhor prática, mas é boa para o código herdado.
Esses problemas surgiram quando muitas pessoas adicionaram seus próprios componentes à interface do usuário e alguns métodos se tornaram realmente massivos, mas não é prático criar 40 métodos de uso único dentro da classe que já foram uma bagunça.
fonte