2010-11-13 13 views
10

Considere usted tiene alguna expresión comoAsignación "doble": ¿debería evitarse?

i = j = 0 

Suponiendo que esto está bien definido en el idioma de su elección. En general, sería mejor dividir esto en dos expresiones como

i = 0 
j = 0 

Veo esto a veces en el código de la biblioteca. No parece que compre mucho en términos de brevedad y no debe tener un rendimiento mejor que las dos declaraciones (aunque eso puede depender del compilador). Entonces, ¿hay alguna razón para usar una sobre la otra? ¿O es solo una preferencia personal? Sé que esto suena como una pregunta tonta, pero me está molestando desde hace mucho tiempo :-).

Respuesta

3

Las dos formas reflejan diferentes puntos de vista en la asignación.

El primer caso trata la asignación (al menos la interna) como un operador (una función que devuelve un valor).

El segundo caso trata la asignación como una declaración (un comando para hacer algo).

En algunos casos, la asignación como operador tiene su punto, sobre todo por brevedad, o para usar en contextos que esperan un resultado. Sin embargo, lo siento confuso. Por varias razones:

  • Operador de asignación son básicamente operadores de efectos secundarios, y hoy en día es un problema optimizarlos para los compiladores. En idiomas como C y C++ llevan a muchos casos de Comportamiento no definido o código no optimizado.
  • No está claro qué debería devolver. ¿Debería el operador de asignación devolver el valor asignado, o debería devolver la dirección del lugar donde se almacenó? Uno u otro podría ser útil, según el contexto.
  • Con asignaciones compuestas como +=, es aún peor. No está claro si el operador debe devolver el valor inicial, el resultado combinado o incluso el lugar donde se almacenó.

La asignación como una declaración a veces conduce a variables intermedias, pero ese es el único inconveniente que veo. Está claro y los compiladores saben cómo optimizar de manera eficiente estas declaraciones sucesivas.

Básicamente, evitaría la asignación como operador siempre que sea posible. El caso presentado es muy simple y no es realmente confuso, pero como regla general, preferiría.

i = 0 
j = 0 

o

i, j = 0, 0 

para los idiomas que soporta, asignación paralelo.

1

La segunda forma es más legible y clara, yo lo prefiero.

Sin embargo trato de evitar la declaración de "doble":

int i, j; 

en lugar de

int i; 
int j; 

si van consecutivamente. Especialmente en caso de MyVeryLong.AndComplexType

+1

no hablar de 'char * i, j; 'contra' char * i, * j; '. –

+0

@Ignacio: Buena pregunta anterior relacionada con su tema http://stackoverflow.com/questions/377164/whats-your-preferred-pointer-declaration-style-and-why – abatishchev

2

Depende del idioma. En los lenguajes altamente orientados a objetos, la doble asignación da como resultado que el mismo objeto se asigne a múltiples variables, de modo que los cambios en una variable se reflejan en la otra.

$ python -c 'a = b = [] ; a.append(1) ; print b' 
[1] 
+0

en tales casos 'b = []; a = b' es la sintaxis más clara – kriss

+0

@kriss: Pero la pregunta no es preguntar acerca de esa sintaxis. –

+0

Estaba señalando que el equivalente de la asignación combinada de una línea no era la asignación de la misma constante a ambos objetos. Esto se basa en la convención de que el resultado de la asignación es el valor que se asignó a la primera variable. Esta es una opción posible sobre muchas. En los lenguajes que soportan los moldes implícitos en la asignación (como C++) ni siquiera es obvio si el valor devuelto debe ser el objeto antes de después de que se realiza el reparto (creo que es el primero, pero tendría que verificar en el documento de referencia estándar que se Por supuesto). – kriss

2

La mayoría de las personas encontrarán ambas posibilidades igualmente legibles. Algunas de estas personas tendrán una preferencia personal de cualquier manera. Pero hay personas que, a primera vista, podrían confundirse por la "doble asignación". Personalmente, me gusta el enfoque por separado, Bacause

  • Es 100% legibles
  • En realidad no está detallado en comparación con el doble variante
  • Permite que me olvide de las reglas de asociatividad para el operador =
8

Había una vez una diferencia de rendimiento, que es una de las razones por las que se utilizó este tipo de asignación. Los compiladores se convertirían en i = 0; j = 0;:

load 0 
store [i] 
load 0 
store [j] 

Así se puede ahorrar una instrucción utilizando i = j = 0 como el compilador convertir esto en:

load 0 
store [j] 
store [i] 

Hoy en día los compiladores pueden hacer este tipo de optimizaciones por sí mismos. Además, como las CPU actuales ejecutan varias instrucciones al mismo tiempo, el rendimiento ya no se puede medir simplemente en número de instrucciones. Las instrucciones en las que una acción no depende del resultado de otra pueden ejecutarse en paralelo, por lo que la versión que usa un valor separado para cada variable podría ser más rápida.

En cuanto al estilo de programación, debe usar la forma que mejor exprese la intención del código.

Puede, por ejemplo, encadenar las asignaciones cuando simplemente quiere borrar algunas variables y separarlas cuando el valor tiene un significado específico. Especialmente si el significado de establecer una variable en el valor es diferente de establecer la otra variable en el mismo valor.

3

En primer lugar, en un nivel semántico, depende de si quiere decir que i y j tienen el mismo valor, o que ambos tienen el mismo valor.

Por ejemplo, si i y j son los índices en una matriz 2D, ambos comienzan en cero. j = i = 0 dice i comienza en cero, y j comienza donde i comenzó. Si quisiera comenzar en la segunda fila, no necesariamente querría comenzar en la segunda columna, así que no los inicializaría en la misma declaración: los índices para las filas y las columnas suceden de manera independiente para que comiencen en cero.

También, en los idiomas donde i y j representan objetos complicados en lugar de las variables integrales, o donde la asignación puede provocar una conversión implícita, no son equivalentes:

#include <iostream> 

class ComplicatedObject 
{ 
public: 
    const ComplicatedObject& operator= (const ComplicatedObject& other) { 
     std::cout << " ComplicatedObject::operator= (const ComplicatedObject&)\n"; 
     return *this; 
    } 
    const ComplicatedObject& operator= (int value) { 
     std::cout << " ComplicatedObject::operator= (int)\n"; 
     return *this; 
    } 

}; 

int main() 
{ 
    { 
     // the functions called are not the same 
     ComplicatedObject i; 
     ComplicatedObject j; 

     std::cout << "j = i = 0:\n"; 
     j = i = 0; 

     std::cout << "i = 0; j = 0:\n"; 
     i = 0; 
     j = 0; 
    } 

    { 
     // the result of the first assignment is 
     // effected by implicit conversion 
     double j; 
     int i; 

     std::cout << "j = i = 0.1:\n"; 
     j = i = 0.1; 

     std::cout << " i == " << i << '\n' 
        << " j == " << j << '\n' 
        ; 

     std::cout << "i = 0.1; j = 0.1:\n"; 
     i = 0.1; 
     j = 0.1; 

     std::cout << " i == " << i << '\n' 
        << " j == " << j << '\n' 
        ; 
    } 

} 
Cuestiones relacionadas