Por que declarar uma variável em uma linha e atribuir a ela na próxima?

98

Muitas vezes vejo no código C e C ++ a seguinte convenção:

some_type val;
val = something;

some_type *ptr = NULL;
ptr = &something_else;

em vez de

some_type val = something;
some_type *ptr = &something_else;

Inicialmente presumi que isso era um hábito remanescente dos dias em que você tinha que declarar todas as variáveis locais no topo do escopo. Mas aprendi a não descartar tão rapidamente os hábitos dos desenvolvedores veteranos. Então, há uma boa razão para declarar em uma linha e atribuir depois?

    
por Jonathan Sterling 07.05.2011 / 22:30
fonte

7 respostas

87

C

No C89 todas as declarações tinham que estar no começo de um escopo ( { ... } ), mas este requisito foi descartado rapidamente (primeiro com extensões de compilador e depois com o padrão).

C ++

Estes exemplos não são os mesmos. some_type val = something; chama o construtor de cópia enquanto val = something; chama o construtor padrão e, em seguida, a função operator= . Essa diferença é frequentemente crítica.

Hábitos

Algumas pessoas preferem primeiro declarar variáveis e depois defini-las, caso estejam reformatando seu código mais tarde com as declarações em um ponto e a definição em outro.

Sobre os ponteiros, algumas pessoas têm o hábito de inicializar todos os ponteiros para NULL ou nullptr , não importa o que eles façam com esse ponteiro.

    
por 07.05.2011 / 22:35
fonte
26

Você marcou sua pergunta C e C ++ ao mesmo tempo, enquanto a resposta é significativamente diferente nesses idiomas.

Em primeiro lugar, o texto do título da sua pergunta está incorreto (ou, mais precisamente, irrelevante para a pergunta em si). Em ambos os exemplos, a variável é declarada e definida simultaneamente, em uma linha. A diferença entre seus exemplos é que no primeiro as variáveis são deixadas não inicializadas ou inicializadas com um valor fictício e então é atribuído um valor significativo mais tarde. No segundo exemplo, as variáveis são inicializadas imediatamente.

Em segundo lugar, na linguagem C ++, como @nightcracker observou em sua resposta, esses dois construtos são semanticamente diferentes. O primeiro depende da inicialização, enquanto o segundo - na atribuição. Em C ++, essas operações são sobrecarregáveis e, portanto, podem potencialmente levar a resultados diferentes (embora se possa notar que produzir sobrecargas não equivalentes de inicialização e designação não é uma boa ideia).

Na linguagem C padrão original (C89 / 90) é ilegal declarar variáveis no meio do bloco, e é por isso que você pode ver variáveis declaradas não inicializadas (ou inicializadas com valores fictícios) no início do bloco e em seguida, atribuir valores significativos posteriormente, quando esses valores significativos ficarem disponíveis.

Na linguagem C99, não há problema em declarar variáveis no meio do bloco (como em C ++), o que significa que a primeira abordagem é necessária apenas em algumas situações específicas em que o inicializador não é conhecido no ponto de declaração. (Isso também se aplica ao C ++).

    
por 07.05.2011 / 22:47
fonte
13

Eu acho que é um hábito antigo, sobra de "declaração local" vezes. E, portanto, como resposta à sua pergunta: Não, não acho que haja uma boa razão. Eu nunca faço isso sozinho.

    
por 07.05.2011 / 22:35
fonte
4

Eu disse algo sobre isso em minha resposta para uma pergunta do Helium3 .

Basicamente, eu digo que é uma ajuda visual para ver facilmente o que mudou.

if (a == 0) {
    struct whatever *myobject = 0;
    /* did 'myobject' (the pointer) get assigned?
    ** or was it '*myobject' (the struct)? */
}

e

if (a == 0) {
    struct whatever *myobject;
    myobject = 0;
    /* 'myobject' (the pointer) got assigned */
}
    
por 07.05.2011 / 22:42
fonte
4

As outras respostas são muito boas. Há alguma história em torno disso em C. Em C ++ há a diferença entre um construtor e um operador de atribuição.

Surpreende-me que ninguém mencione o ponto adicional: manter as declarações separadas do uso de uma variável pode, às vezes, ser muito mais legível.

Visualmente falando, ao ler o código, os artefatos mais comuns, como os tipos e nomes de variáveis, não são o que surgem em você. São as afirmações em que geralmente você está mais interessado, passam a maior parte do tempo olhando para elas e, portanto, há uma tendência a olhar para o resto.

Se eu tiver alguns tipos, nomes e atribuições acontecendo no mesmo espaço apertado, é um pouco de sobrecarga de informações. Além disso, significa que algo importante está acontecendo no espaço que eu geralmente olho.

Pode parecer um pouco contra-intuitivo dizer, mas este é um caso em que fazer com que sua fonte ocupe mais espaço vertical pode torná-la melhor. Eu vejo isso como semelhante a por que você não deve escrever linhas cheias de coisas que fazem quantidades loucas de aritmética e atribuição de ponteiros em um espaço vertical apertado - só porque a linguagem permite que você consiga essas coisas não significa que você deva fazer isso o tempo todo. : -)

    
por 08.05.2011 / 00:01
fonte
2

Em C, essa era a prática padrão, porque as variáveis tinham que ser declaradas no início da função, diferentemente de C ++, onde poderiam ser declaradas em qualquer lugar do corpo da função a ser usado posteriormente. Os ponteiros foram definidos como 0 ou NULL, porque apenas garantiu que o ponteiro apontasse para nenhum lixo. Caso contrário, não há vantagem significativa que eu possa pensar, o que obriga qualquer pessoa a fazer assim.

    
por 07.05.2011 / 22:39
fonte
2

Prove a localização de definições de variáveis e sua inicialização significativa:

  • se as variáveis são habitualmente atribuídas a um valor significativo quando aparecem pela primeira vez no código (outra perspectiva sobre a mesma coisa: você adia a aparência até que um valor significativo esteja disponível), então não há valor sem significado ou não inicializado (o que pode acontecer facilmente é que alguma inicialização seja ignorada acidentalmente devido a declarações condicionais, avaliação de curto-circuito, exceções, etc.)

  • pode ser mais eficiente

    • evita overheads de definir valor inicial (construção padrão ou inicialização para algum valor sentinela como NULL)
    • operator= às vezes pode ser menos eficiente e requer um objeto temporário
    • às vezes (especialmente para funções embutidas) o otimizador pode remover algumas / todas as ineficiências

  • minimizar o escopo das variáveis, por sua vez, minimiza o número médio de variáveis simultaneamente no escopo :

    • torna mais fácil rastrear mentalmente as variáveis no escopo, os fluxos de execução e as instruções que podem afetar essas variáveis e a importação de seu valor
    • pelo menos para alguns objetos complexos e opacos, isso reduz o uso de recursos (heap, encadeamentos, memória compartilhada, descritores) do programa
  • às vezes, mais conciso, pois você não está repetindo o nome da variável em uma definição e, em seguida, em uma atribuição significativa inicial

  • necessário para determinados tipos, como referências, e quando você deseja que o objeto seja const

Argumentos para agrupar definições de variáveis:

  • às vezes é conveniente e / ou conciso fatorar o tipo de um número de variáveis:

    the_same_type v1, v2, v3;

    (se o motivo for apenas que o nome do tipo é excessivamente longo ou complexo, um typedef às vezes pode ser melhor)

  • às vezes é desejável agrupar variáveis independentemente de seu uso para enfatizar o conjunto de variáveis (e tipos) envolvidas em alguma operação:

    type v1; e type v2; type v3;

    Isso enfatiza a semelhança de tipos e torna um pouco mais fácil alterá-los, enquanto ainda mantém uma variável por linha, o que facilita a copiar e colar, // de comentários, etc.

Como é frequentemente o caso na programação, embora possa haver um claro benefício empírico para uma prática na maioria das situações, a outra prática realmente pode ser incrivelmente melhor em alguns casos.

    
por 09.05.2011 / 11:57
fonte

Tags