Pergunta 1: declarar uma variável dentro de um loop é uma boa ou má prática?
Eu li os outros tópicos sobre se há ou não um problema de desempenho (a maioria disse não) e que você deve sempre declarar variáveis o mais próximo possível de onde elas serão usadas. O que eu quero saber é se isso deve ou não ser evitado ou se é realmente preferido.
Exemplo:
for(int counter = 0; counter <= 10; counter++)
{
string someString = "testing";
cout << someString;
}
Pergunta 2: A maioria dos compiladores percebe que a variável já foi declarada e apenas pula essa parte ou cria um local para ela na memória toda vez?
c++
loops
variable-declaration
JeramyRR
fonte
fonte
Respostas:
Esta é uma excelente prática.
Ao criar variáveis dentro de loops, você garante que seu escopo seja restrito a dentro do loop. Não pode ser referenciado nem chamado fora do loop.
Deste jeito:
Se o nome da variável for um pouco "genérico" (como "i"), não há risco de misturá-la com outra variável com o mesmo nome em algum lugar posteriormente no seu código (também pode ser mitigado usando a
-Wshadow
instrução de aviso no GCC)O compilador sabe que o escopo da variável está limitado ao interior do loop e, portanto, emitirá uma mensagem de erro adequada se a variável for referenciada por engano em outro local.
Por último, mas não menos importante, alguma otimização dedicada pode ser executada com mais eficiência pelo compilador (o mais importante é alocar o registro), pois sabe que a variável não pode ser usada fora do loop. Por exemplo, não há necessidade de armazenar o resultado para reutilização posterior.
Em suma, você está certo em fazê-lo.
Observe, no entanto, que a variável é não deve manter seu valor entre cada loop. Nesse caso, pode ser necessário inicializá-lo sempre. Você também pode criar um bloco maior, abrangendo o loop, cujo único objetivo é declarar variáveis que devem manter seu valor de um loop para outro. Isso normalmente inclui o próprio contador de loop.
Para a pergunta 2: A variável é alocada uma vez, quando a função é chamada. De fato, de uma perspectiva de alocação, é (quase) o mesmo que declarar a variável no início da função. A única diferença é o escopo: a variável não pode ser usada fora do loop. Pode até ser possível que a variável não esteja alocada, apenas reutilizando algum espaço livre (de outra variável cujo escopo terminou).
Com escopo restrito e preciso, otimizações mais precisas. Mais importante, porém, isso torna seu código mais seguro, com menos estados (ou seja, variáveis) com que se preocupar ao ler outras partes do código.
Isso é verdade mesmo fora de um
if(){...}
bloco. Normalmente, em vez de:é mais seguro escrever:
A diferença pode parecer pequena, especialmente em um exemplo tão pequeno. Mas em uma base de código maior, ajudará: agora não há risco de transportar algum
result
valor def1()
paraf2()
bloquear. Cada umresult
é estritamente limitado ao seu próprio escopo, tornando seu papel mais preciso. Do ponto de vista do revisor, é muito melhor, pois ele tem menos variáveis de estado de longo alcance para se preocupar e acompanhar.Até o compilador ajudará melhor: supondo que, no futuro, após alguma alteração incorreta de código,
result
não seja adequadamente inicializadof2()
. A segunda versão simplesmente se recusará a trabalhar, declarando uma mensagem de erro clara em tempo de compilação (muito melhor que o tempo de execução). A primeira versão não localizará nada, o resultado def1()
simplesmente será testado uma segunda vez, sendo confundido pelo resultado def2()
.Informação complementar
A ferramenta de código aberto CppCheck (uma ferramenta de análise estática para código C / C ++) fornece algumas dicas excelentes sobre o escopo ideal das variáveis.
Em resposta ao comentário sobre alocação: A regra acima é verdadeira em C, mas pode não ser para algumas classes C ++.
Para tipos e estruturas padrão, o tamanho da variável é conhecido no momento da compilação. Não existe "construção" em C; portanto, o espaço para a variável será simplesmente alocado na pilha (sem nenhuma inicialização), quando a função é chamada. É por isso que existe um custo "zero" ao declarar a variável dentro de um loop.
No entanto, para as classes C ++, existe esse construtor sobre o qual sei muito menos. Acho que a alocação provavelmente não será o problema, pois o compilador deve ser inteligente o suficiente para reutilizar o mesmo espaço, mas é provável que a inicialização ocorra a cada iteração do loop.
fonte
string
evector
especificamente, o operador de atribuição pode reutilizar o buffer alocado em cada loop, o que (dependendo do seu loop) pode ser uma enorme economia de tempo.Geralmente, é uma prática muito boa mantê-lo muito próximo.
Em alguns casos, haverá uma consideração como o desempenho que justifica a retirada da variável do loop.
No seu exemplo, o programa cria e destrói a string a cada vez. Algumas bibliotecas usam uma otimização de cadeia de caracteres pequena (SSO), portanto, a alocação dinâmica pode ser evitada em alguns casos.
Suponha que você queira evitar essas criações / alocações redundantes, escreva-as como:
ou você pode extrair a constante:
Ele pode reutilizar o espaço que a variável consome e pode puxar invariantes para fora do seu loop. No caso da matriz const char (acima) - essa matriz pode ser removida. No entanto, o construtor e o destruidor devem ser executados a cada iteração no caso de um objeto (como
std::string
). No caso destd::string
, esse 'espaço' inclui um ponteiro que contém a alocação dinâmica que representa os caracteres. Então, é isso:exigiria cópia redundante em cada caso e alocação dinâmica e livre se a variável ficar acima do limite para a contagem de caracteres SSO (e o SSO for implementado pela sua biblioteca std).
Fazendo isso:
ainda exigiria uma cópia física dos caracteres em cada iteração, mas o formulário pode resultar em uma alocação dinâmica, porque você atribui a string e a implementação deve verificar que não há necessidade de redimensionar a alocação de backup da string. Obviamente, você não faria isso neste exemplo (porque várias alternativas superiores já foram demonstradas), mas você pode considerá-lo quando a string ou o conteúdo do vetor variar.
Então, o que você faz com todas essas opções (e mais)? Mantenha-o muito próximo como padrão - até você entender bem os custos e saber quando deve se desviar.
fonte
Para C ++, depende do que você está fazendo. OK, é um código estúpido, mas imagine
Você esperará 55 segundos até obter a saída do myFunc. Só porque cada construtor e destruidor de loop juntos precisam de 5 segundos para terminar.
Você precisará de 5 segundos até obter a saída do myOtherFunc.
Claro, este é um exemplo louco.
Mas ilustra que pode se tornar um problema de desempenho quando cada loop é feito da mesma construção quando o construtor e / ou destruidor precisa de algum tempo.
fonte
Não postei para responder às perguntas de JeremyRR (como elas já foram respondidas); em vez disso, postei apenas para dar uma sugestão.
Para o JeremyRR, você pode fazer o seguinte:
Não sei se você percebeu (não sabia quando comecei a programar) que colchetes (desde que estejam em pares) podem ser colocados em qualquer lugar do código, não apenas depois de "se", "para", " enquanto ", etc.
Meu código compilado no Microsoft Visual C ++ 2010 Express, então eu sei que funciona; além disso, tentei usar a variável fora dos colchetes em que ela estava definida e recebi um erro, por isso sei que a variável foi "destruída".
Não sei se é uma má prática usar esse método, pois muitos colchetes não rotulados podem rapidamente tornar o código ilegível, mas talvez alguns comentários possam esclarecer as coisas.
fonte
É uma prática muito boa, pois todas as respostas acima fornecem um aspecto teórico muito bom da pergunta, deixe-me dar uma idéia do código. Estava tentando resolver o DFS sobre GEEKSFORGEEKS, encontrei o problema de otimização ...... Se você tentar resolver o código que declara o número inteiro fora do loop fornece erro de otimização.
Agora coloque números inteiros dentro do loop, para obter a resposta correta ...
isso reflete completamente o que o senhor @justin estava dizendo no segundo comentário .... tente isso aqui https://practice.geeksforgeeks.org/problems/depth-first-traversal-for-a-graph/1 . basta dar uma chance ... você vai conseguir. Espero essa ajuda.
fonte
flag
deve ser reinicializado em 0 a cadawhile
iteração. Esse é um problema lógico, não um problema de definição.Capítulo 4.8 Estrutura de blocos na K & R's The C Programming Language 2.Ed. :
Eu poderia ter deixado de ver a descrição relevante no livro, como:
Mas um teste simples pode provar a suposição:
fonte