c++ - utilizan - que es if else en programacion
¿Cuál es el propósito de usar llaves(es decir,{}) para una sola línea si o bucle? (23)
El ejemplo más pertinente que se me ocurre:
if(someCondition)
if(someOtherCondition)
DoSomething();
else
DoSomethingElse();
¿Con qué if
va a emparejar la else
? La sangría implica que el externo if
obtiene el else
, pero no es así como el compilador lo verá; lo interno if
obtendremos lo else
, y lo externo if
no lo consigue. Tendría que saber que (o verlo comportarse de esa manera en el modo de depuración) para descubrir por inspección por qué este código puede estar fallando en sus expectativas. Se vuelve más confuso si conoces Python; en ese caso, usted sabe que la sangría define los bloques de código, por lo que esperaría que se evalúe de acuerdo con la sangría. C #, sin embargo, no da una vuelta volante sobre espacios en blanco.
Ahora, dicho esto, no estoy particularmente de acuerdo con esta regla de "siempre usar corchetes" en su cara. Hace que el código sea muy ruidoso verticalmente, lo que reduce la capacidad de leerlo rápidamente. Si la declaración es:
if(someCondition)
DoSomething();
... entonces debería escribirse así. La frase "usar siempre corchetes" suena como "rodear siempre las operaciones matemáticas con paréntesis". Eso convertiría la simple declaración a * b + c / d
en ((a * b) + (c / d))
, introduciendo la posibilidad de perder un paren cercano (la pesadilla de muchos programadores), y para qué ? El orden de las operaciones es bien conocido y se aplica bien, por lo que los paréntesis son redundantes. Solo usaría paréntesis para imponer un orden de operaciones diferente al que normalmente se aplicaría: a * (b+c) / d
por ejemplo. Las llaves de bloque son similares; úselos para definir lo que quiere hacer en los casos en que difiere del valor predeterminado, y no es "obvio" (subjetivo, pero por lo general es bastante común).
Estoy leyendo algunas notas de la conferencia de mi profesor de C ++ y él escribió lo siguiente:
- Use sangría // OK
- Nunca confíe en la precedencia del operador - Use siempre paréntesis // OK
- Siempre use un bloque {}, incluso para una sola línea // no está bien , ¿por qué?
- Const objeto en el lado izquierdo de la comparación // OK
- Utilice unsigned para variables que son> = 0 // buen truco
- Establezca el puntero en NULL después de la eliminación - Protección de eliminación doble // no está mal
La tercera técnica no me queda clara: ¿qué ganaría al colocar una línea en un { ... }
?
Por ejemplo, toma este código extraño:
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
if (i % 2 == 0)
{
j++;
}
}
y reemplazarlo con:
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
if (i % 2 == 0)
j++;
¿Cuál es el beneficio de usar la primera versión?
Es más intuitivo y fácilmente comprensible. Aclara la intención.
Y garantiza que el código no se rompa cuando un nuevo usuario puede extrañar, sin saberlo, el {
, }
al agregar una nueva declaración de código.
Es muy fácil cambiar accidentalmente el flujo de control con comentarios si no usa {
y }
. Por ejemplo:
if (condition)
do_something();
else
do_something_else();
must_always_do_this();
Si comenta do_something_else()
con un solo comentario de línea, terminará con esto:
if (condition)
do_something();
else
//do_something_else();
must_always_do_this();
Se compila, pero must_always_do_this()
no siempre se llama.
Tuvimos este problema en nuestra base de código, donde alguien ingresó para deshabilitar algunas funciones muy rápidamente antes del lanzamiento. Afortunadamente lo atrapamos en la revisión del código.
Estoy usando {}
todas partes excepto en algunos casos donde es obvio. Una sola línea es uno de los casos:
if(condition) return; // OK
if(condition) //
return; // and this is not a one-liner
Puede lastimarte cuando agregas algún método antes de volver. La sangría indica que el retorno se está ejecutando cuando se cumple la condición, pero se devolverá siempre.
Otro ejemplo en C # con el uso de la leyenda
using (D d = new D()) // OK
using (C c = new C(d))
{
c.UseLimitedResource();
}
que es equivalente a
using (D d = new D())
{
using (C c = new C(d))
{
c.UseLimitedResource();
}
}
Intentemos modificar también i
cuando incrementamos j
:
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
if (i % 2 == 0)
j++;
i++;
¡Oh no! Viniendo de Python, esto se ve bien, pero en realidad no lo es, ya que es equivalente a:
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
if (i % 2 == 0)
j++;
i++;
Por supuesto, este es un error tonto, pero que incluso un programador experimentado podría cometer.
Otra muy buena razón se señala en la respuesta de ta.speot.is .
Un tercero en el que puedo pensar está anidado if
es:
if (cond1)
if (cond2)
doSomething();
Ahora, supongamos que ahora desea doSomethingElse()
cuando no se cumple cond1
(nueva característica). Asi que:
if (cond1)
if (cond2)
doSomething();
else
doSomethingElse();
lo que obviamente es incorrecto, ya que la else
asocia con el interior if
.
Edit: Ya que esto está recibiendo algo de atención, aclararé mi punto de vista. La pregunta que estaba respondiendo es:
¿Cuál es el beneficio de usar la primera versión?
Que he descrito Hay algunos beneficios. Pero, IMO, las reglas "siempre" no siempre se aplican. Así que no lo apoyo totalmente
Siempre use un bloque {}, incluso para una sola línea // no está bien, ¿por qué?
No estoy diciendo que use siempre un bloque {}
. Si es una condición y comportamiento bastante simple, no lo hagas. Si sospecha que alguien podría venir más tarde y cambiar su código para agregar funcionalidad, haga.
La base de código en la que estoy trabajando está dispersa de código por personas con una aversión patológica a los aparatos ortopédicos, y para las personas que llegan más tarde, realmente puede marcar la diferencia en cuanto a la capacidad de mantenimiento.
El ejemplo problemático más frecuente que he encontrado es el siguiente:
if ( really incredibly stupidly massively long statement that exceeds the width of the editor) do_foo;
this_looks_like_a_then-statement_but_isn''t;
Así que cuando vengo y deseo agregar una declaración de entonces, puedo terminar fácilmente con esto si no tengo cuidado:
if ( really incredibly stupidly massively long statement that exceeds the width of the editor) do_foo;
{
this_looks_like_a_then-statement_but_isn''t;
i_want_this_to_be_a_then-statement_but_it''s_not;
}
Dado que se necesitan ~ 1 segundo para agregar llaves y puede ahorrarle una depuración de minutos confusos como mínimo, ¿por qué nunca iría con la opción de ambigüedad reducida? Me parece una falsa economía.
Mi 2c:
Usar sangría
Obviamente
Nunca confíe en la precedencia del operador - Use siempre paréntesis
No usaría las palabras "nunca y" siempre ", pero en general veo que esta regla es útil. En algunos idiomas (Lisp, Smalltalk) esto no es un problema.
Siempre use un bloque {}, incluso para una sola línea
Nunca hago eso y nunca tuve un solo problema, pero puedo ver cómo puede ser bueno para los estudiantes, especialmente. Si estudiaban Python antes.
Const objeto en el lado izquierdo de comparación
¿Condiciones de Yoda? No por favor. Duele la legibilidad. Solo use el nivel máximo de advertencia cuando compile su código.
Use unsigned para variables que son> = 0
DE ACUERDO. Lo suficientemente divertido, he escuchado a Stroustrup en desacuerdo.
Establezca el puntero en NULL después de la eliminación - Protección de eliminación doble
¡Mal consejo! Nunca tenga un puntero que apunte a un objeto eliminado o no existente.
Mirando a través de las respuestas, nadie declaró explícitamente el tipo de práctica que hago, contando la historia de su código:
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
if (i % 2 == 0)
{
j++;
}
}
Se convierte en
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
if (i % 2 == 0) j++;
}
Poner el j++
en la misma línea que el if debería indicar a cualquier otra persona: "Solo quiero que este bloque incremente j
" . Por supuesto, esto solo vale la pena si la línea es lo más simplista posible, porque poner un punto de interrupción aquí, como se menciona, no va a ser muy útil.
De hecho, acabo de encontrar parte de la API de Twitter Storm que tiene este ''tipo'' de código en java, aquí está el fragmento principal del código de ejecución, en la página 43 de esta presentación :
...
Integer Count = counts.get(word);
if (Count=null) count=0;
count++
...
El bloque de bucle for tiene dos cosas, por lo que no incluiría ese código. Es decir nunca
int j = 0;
for (int i = 0 ; i < 100 ; ++i) if (i % 2 == 0) j++;
Es horrible y ni siquiera sé si funciona (según lo previsto); no hagas esto Las nuevas líneas y llaves ayudan a distinguir piezas de código separadas pero relacionadas, de la misma manera que lo hacen una coma o un punto y coma en prosa. El bloque anterior es tan malo como una oración realmente larga con algunas cláusulas y algunas otras afirmaciones que nunca se rompen o se detienen para distinguir partes separadas.
Si realmente desea telegrafiar a otra persona, es un trabajo de una sola línea, use un operador ternario o ?:
Form:
for (int i = 0 ; i < 100 ; ++i) (i%2 ? 0 : >0) j++;
Pero esto está al borde del código de golf, y creo que no es una buena práctica (no me queda claro si debería poner el j ++ en un lado de la :
o no). NB : no he ejecutado un operador ternario en C ++ antes, no sé si esto funciona, pero existe .
En breve:
Imagine cómo su lector (es decir, la persona que mantiene el código) interpreta su historia (código). Hazlo lo más claro posible para ellos. Si sabe que el programador / estudiante novato está manteniendo esto, quizás incluso deje la mayor cantidad posible de {}
, para que no se confundan.
Para agregar a las sugerencias más sensatas de arriba, un ejemplo que encontré al refactorizar un código donde esto se vuelve crítico fue el siguiente: estaba alterando una base de código muy grande para cambiar de una API a otra. La primera API tenía una llamada para establecer la identificación de la compañía de la siguiente manera:
setCompIds( const std::string& compId, const std::string& compSubId );
mientras que el reemplazo necesitaba dos llamadas:
setCompId( const std::string& compId );
setCompSubId( const std::string& compSubId );
Me propuse cambiar esto usando expresiones regulares que tuvieron mucho éxito. También pasamos el código a través de astyle , lo que realmente lo hizo mucho más legible. Luego, a mitad del proceso de revisión, descubrí que en algunas circunstancias condicionales estaba cambiando esto:
if ( condition )
setCompIds( compId, compSubId );
A esto:
if ( condition )
setCompId( compId );
setCompSubId( compSubId );
que claramente no es lo que se requería. Tuve que volver al principio, hacer esto de nuevo tratando el reemplazo como completamente dentro de un bloque y luego alterando manualmente cualquier cosa que terminara viéndome ridícula (al menos no sería incorrecto).
Me doy cuenta de que astyle ahora tiene la opción --add-brackets
que le permite agregar corchetes donde no hay ninguno y lo recomiendo encarecidamente si alguna vez se encuentra en la misma posición que yo.
Tengo mis dudas sobre la competencia del profesor. Teniendo en cuenta sus puntos:
- DE ACUERDO
- ¿Alguien realmente escribiría (o querría leer)
(b*b) - ((4*a)*c)
? Algunas precedencias son obvias (o deberían ser), y los paréntesis adicionales simplemente aumentan la confusión. (Por otro lado, usted debería usar los paréntesis en casos menos obvios, incluso si sabe que no son necesarios). - Una especie de Existen dos convenciones de amplia difusión para el formato de condicionales y bucles:
if ( cond ) { code; } y:
if ( cond ) { code; } En el primero, estaría de acuerdo con él. La apertura
{
no es tan visible, por lo que es mejor asumir que siempre está ahí. En el segundo, sin embargo, yo (y la mayoría de las personas con las que he trabajado) no tengo ningún problema en omitir las llaves para una sola frase. (Siempre que la sangría sea sistemática y que utilices este estilo de manera consistente. (Y muchos programadores muy buenos, que escriben códigos muy legibles, omiten las llaves incluso al formatear de la primera manera). - No Cosas como
if ( NULL == ptr )
son lo suficientemente feas como para dificultar la legibilidad. Escribe las comparaciones de forma intuitiva. (Lo que en muchos casos resulta en la constante de la derecha). Su 4 es un mal consejo; cualquier cosa que haga que el código no sea natural lo hace menos legible. - No Cualquier cosa excepto
int
está reservada para casos especiales. Para los programadores experimentados de C y C ++, el uso de operadores de bit de señalesunsigned
. C ++ no tiene un tipo cardinal real (o cualquier otro tipo de subrango efectivo);unsigned
no funciona para valores numéricos, debido a las reglas de promoción. Los valores numéricos en los que no tendría sentido ninguna operación aritmética, como los números de serie, presumiblemente podrían estarunsigned
. Sin embargo, me opondría a ello porque envía el mensaje incorrecto: las operaciones a nivel de bits tampoco tienen sentido. La regla básica es que los tipos integrales sonint
, _lessless_ hay una razón importante para usar otro tipo. - No Hacer esto sistemáticamente es engañoso, y en realidad no protege contra nada. En estricto código OO,
delete this;
a menudo es el caso más frecuente (y no puede establecerlo enNULL
), y de lo contrario, la mayoría de losdelete
están en los destructores, por lo que no puede acceder al puntero más tarde de todos modos. Y establecerlo enNULL
no hace nada con respecto a otros punteros que flotan alrededor. Establecer el puntero sistemáticamente enNULL
da una falsa sensación de seguridad y realmente no le compra nada.
Mira el código en cualquiera de las referencias típicas. Stroustrup viola todas las reglas que has dado, excepto la primera, por ejemplo.
Te sugiero que encuentres otro profesor. Uno que realmente sabe de lo que está hablando.
Todas las otras respuestas defienden la regla de tu profesor 3.
Permítame decirle que estoy de acuerdo con usted: la regla es redundante y no lo recomendaría. Es cierto que, en teoría, evita errores si siempre se agregan llaves. Por otro lado, nunca he encontrado este problema en la vida real : al contrario de lo que implican otras respuestas, una vez no me he olvidado de agregar los corchetes una vez que se hicieron necesarios. Si usa la sangría adecuada, se vuelve inmediatamente obvio que necesita agregar llaves una vez que se sangra una declaración.
La respuesta del "Componente 10" en realidad resalta el único caso concebible en el que esto realmente podría llevar a un error. Pero, por otro lado, reemplazar el código a través de expresiones regulares siempre merece un enorme cuidado de todos modos.
Ahora veamos el otro lado de la medalla: ¿hay una desventaja de usar siempre llaves? Las otras respuestas simplemente ignoran este punto. Pero hay una desventaja: ocupa mucho espacio vertical en la pantalla, y esto a su vez puede hacer que su código sea ilegible porque significa que tiene que desplazarse más de lo necesario.
Considere una función con muchas cláusulas de protección al principio (y sí, el siguiente es un código C ++ incorrecto, pero en otros idiomas, esta sería una situación bastante común):
void some_method(obj* a, obj* b)
{
if (a == nullptr)
{
throw null_ptr_error("a");
}
if (b == nullptr)
{
throw null_ptr_error("b");
}
if (a == b)
{
throw logic_error("Cannot do method on identical objects");
}
if (not a->precondition_met())
{
throw logic_error("Precondition for a not met");
}
a->do_something_with(b);
}
Este es un código horrible, y sostengo firmemente que lo siguiente es mucho más legible:
void some_method(obj* a, obj* b)
{
if (a == nullptr)
throw null_ptr_error("a");
if (b == nullptr)
throw null_ptr_error("b");
if (a == b)
throw logic_error("Cannot do method on identical objects");
if (not a->precondition_met())
throw logic_error("Precondition for a not met");
a->do_something_with(b);
}
De manera similar, los bucles anidados cortos se benefician de la omisión de los paréntesis:
matrix operator +(matrix const& a, matrix const& b) {
matrix c(a.w(), a.h());
for (auto i = 0; i < a.w(); ++i)
for (auto j = 0; j < a.h(); ++j)
c(i, j) = a(i, j) + b(i, j);
return c;
}
Comparar con:
matrix operator +(matrix const& a, matrix const& b) {
matrix c(a.w(), a.h());
for (auto i = 0; i < a.w(); ++i)
{
for (auto j = 0; j < a.h(); ++j)
{
c(i, j) = a(i, j) + b(i, j);
}
}
return c;
}
El primer código es conciso; El segundo código está hinchado.
Y sí, esto se puede mitigar hasta cierto punto colocando la abrazadera de apertura en la línea anterior. Pero eso sería aún menos legible que el código sin ningún corchete.
En resumen: no escriba código innecesario que ocupe espacio en la pantalla.
Es mejor establecer el puntero en NULL cuando haya terminado con él.
Aquí hay un ejemplo de por qué:
La clase A hace lo siguiente:
- Asigna un bloque de memoria.
- Luego, algún tiempo después, elimina este bloque de memoria pero no establece el puntero en NULL.
La clase B hace lo siguiente
- Asigna memoria (y, en este caso, se le da el mismo bloque de memoria que se eliminó en la clase A.)
En este punto, tanto la Clase A como la Clase B tienen punteros que apuntan al mismo bloque de memoria, en lo que respecta a la Clase A, este bloque de memoria no existe porque está terminado con él.
Considere el siguiente problema:
¿Qué pasaría si hubiera un error lógico en la Clase A que provocara que se escribiera en la memoria que ahora pertenece a la Clase B?
En este caso particular, no obtendrá un error de excepción de acceso erróneo porque la dirección de memoria es legal, mientras que la clase A ahora está corrompiendo efectivamente los datos de la clase B.
La Clase B puede fallar eventualmente si encuentra valores inesperados y cuando falla, lo más probable es que pase mucho tiempo cazando este error en la Clase B cuando el problema está en la Clase A.
Si hubiera establecido el puntero de memoria eliminado en NULL, habría recibido un error de excepción tan pronto como cualquier error lógico en la Clase A intentara escribir en el puntero NULL.
Si le preocupa el error lógico con la eliminación doble cuando los punteros están en NULL por segunda vez, agregue aserción para esto.
También: Si va a votar en contra, explique.
Considero que el primero está claro y luego el segundo. Da la sensación de que hay instrucciones de cierre, con poco código está bien cuando el código se complica {...}
mucho, incluso si es endif
o no.begin...end
//first
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
if (i % 2 == 0)
{
j++;
}
}
//second
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
if (i % 2 == 0)
j++;
i++;
Hace que su código sea más legible al definir claramente el alcance de sus bucles y bloques condicionales. También te salva de errores accidentales.
Hay varias formas posibles de escribir declaraciones de control; ciertas combinaciones de ellos pueden coexistir sin perjudicar la legibilidad, pero otras combinaciones causarán problemas. El estilo
if (condition)
statement;
coexistirá cómodamente con algunas de las otras formas de escribir declaraciones de control, pero no tan bien con otras. Si las declaraciones controladas multilínea se escriben como:
if (condition)
{
statement;
statement;
}
entonces será visualmente obvio qué if
declaraciones controlan una sola línea y cuáles controlan múltiples líneas. Sin embargo, si las if
declaraciones de varias líneas se escriben como:
if (condition) {
statement;
statement;
}
entonces la probabilidad de que alguien intente extender una if
construcción de una sola declaración sin agregar las llaves necesarias puede ser mucho mayor.
La instrucción de una sola frase en la siguiente línea if
también puede ser problemática si el código base hace un uso significativo del formulario
if (condition) statement;
Mi preferencia es que tener la declaración en su propia línea generalmente mejora la legibilidad, excepto en los casos en que hay muchas if
declaraciones con bloques de control similares, por ejemplo
if (x1 > xmax) x1 = xmax;
if (x1 < xmin) x1 = xmin;
if (x2 > xmax) x2 = xmax;
if (x2 < xmin) x2 = xmin;
etc.
en cuyo caso generalmente precederé y seguiré dichos grupos de if
declaraciones con una línea en blanco para separarlos visualmente de otros códigos. Tener una serie de afirmaciones con las que comenzar if
en la misma sangría proporcionará una clara indicación visual de que hay algo inusual.
Me gusta la respuesta aceptada de Luchian, de hecho, aprendí de la manera más difícil que él tiene razón, por lo que siempre uso llaves, incluso para bloques de una sola línea. Sin embargo, personalmente hago una excepción al escribir un filtro, como está en su ejemplo. Esta:
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
if (i % 2 == 0)
{
j++;
}
}
Me parece abarrotada. Separa el bucle for y la instrucción if en acciones separadas, cuando en realidad su intento es una sola acción: contar todos los enteros divisibles por 2. En un lenguaje más expresivo, esto podría escribirse como:
j = [1..100].filter(_%2 == 0).Count
En idiomas que carecen de cierres, el filtro no se puede expresar en una sola declaración, sino que debe ser un bucle for seguido de una sentencia if. Sin embargo, sigue siendo una acción en la mente del programador, y creo que eso debería reflejarse en el código, de esta manera:
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
if (i % 2 == 0)
{
j++;
}
Otro ejemplo de la adición de llaves. Una vez busqué un error y encontré dicho código:
void SomeSimpleEventHandler()
{
SomeStatementAtTheBeginningNumber1;
if (conditionX) SomeRegularStatement;
SomeStatementAtTheBeginningNumber2;
SomeStatementAtTheBeginningNumber3;
if (!SomeConditionIsMet()) return;
OtherwiseSomeAdditionalStatement1;
OtherwiseSomeAdditionalStatement2;
OtherwiseSomeAdditionalStatement3;
}
Si lee el método línea por línea, notará que hay una condición en el método que se devuelve si no es cierto. Pero en realidad se parece a otros 100 controladores de eventos simples que establecen algunas variables en función de algunas condiciones. Y un día, el Fast Coder entra y agrega una declaración de configuración de variable adicional al final del método:
{
...
OtherwiseSomeAdditionalStatement3;
SetAnotherVariableUnconditionnaly;
}
Como resultado, SetAnotherVariableUnconditionnaly se ejecuta cuando SomeConditionIsMet (), pero el tío rápido no lo notó porque todas las líneas son casi de tamaño similar e incluso cuando la condición de retorno tiene sangría vertical, no es tan notable.
Si el retorno condicional se formatea así:
if (!SomeConditionIsMet())
{
return;
}
es muy notable y el codificador rápido lo encontrará de un vistazo.
Porque cuando tienes dos afirmaciones sin {}
él, es fácil perder un problema. Supongamos que el código se ve así.
int error = 0;
enum hash_type hash = SHA256;
struct hash_value *hash_result = hash_allocate();
if ((err = prepare_hash(hash, &hash_result))) != 0)
goto fail;
if ((err = hash_update(&hash_result, &client_random)) != 0)
goto fail;
if ((err = hash_update(&hash_result, &server_random)) != 0)
goto fail;
if ((err = hash_update(&hash_result, &exchange_params)) != 0)
goto fail;
goto fail;
if ((err = hash_finish(hash)) != 0)
goto fail;
error = do_important_stuff_with(hash);
fail:
hash_free(hash);
return error;
Se ve bien. El problema con él es realmente fácil de pasar por alto, especialmente cuando la función que contiene el código es mucho más grande. El problema es que goto fail
se ejecuta incondicionalmente. Puedes imaginar fácilmente lo frustrante que es esto (haciéndote preguntar por qué la última hash_update
falla siempre, después de que todo se ve bien en hash_update
función)
Sin embargo, eso no significa que sea para agregar en {}
todas partes (en mi opinión, ver en {}
todas partes es molesto). Si bien puede causar problemas, nunca lo hizo para mis propios proyectos, ya que mi estilo de codificación personal prohíbe las condicionales sin {}
que estén en la misma línea (sí, estoy de acuerdo en que mi estilo de codificación no es convencional, pero me gusta y use el estilo de código del proyecto cuando contribuya a otros proyectos). Esto hace que el siguiente código sea correcto.
if (something) goto fail;
Pero no la siguiente.
if (something)
goto fail;
Si eres un compilador, no hace ninguna diferencia. Ambos son los mismos.
Pero para los programadores, el primero es más claro, fácil de leer y menos propenso a errores.
Siempre tener llaves es una regla muy simple y robusta. Sin embargo, el código puede parecer poco elegante cuando hay muchas llaves. Si las reglas permiten omitir las llaves, debería haber reglas de estilo más detalladas y herramientas más sofisticadas. De lo contrario, puede resultar fácilmente con un código caótico y confuso (no elegante). Por lo tanto, buscar reglas de un solo estilo separadas del resto de guías de estilo y herramientas utilizadas es probablemente infructuoso. Solo traeré algunos detalles importantes sobre esa regla # 3 que ni siquiera se han mencionado en otras respuestas.
El primer detalle interesante es que la mayoría de los defensores de esa regla aceptan violarla en el caso de else
. En otras palabras, no exigen en revisión dicho código:
// pedantic rule #3
if ( command == Eat )
{
eat();
}
else
{
if ( command == Sleep )
{
sleep();
}
else
{
if ( command == Drink )
{
drink();
}
else
{
complain_about_unknown_command();
}
}
}
En cambio, si lo ven, pueden incluso sugerir que lo escriban así:
// not fully conforming to rule #3
if ( command == Eat )
{
eat();
}
else if ( command == Sleep )
{
sleep();
}
else if ( command == Drink )
{
drink();
}
else
{
complain_about_unknown_command();
}
Eso es técnicamente una violación de esa regla, ya que no hay llaves entre else
y if
. Dicha dualidad de la regla surge cuando se intenta aplicarla a la base de código automáticamente con una herramienta sin sentido. De hecho, por qué discutir, simplemente deje que una herramienta aplique el estilo automáticamente.
El segundo detalle (que también es a menudo olvidado por los defensores de esa regla) es que los errores que pueden ocurrir nunca se deben únicamente a violaciones de esa regla # 3. En realidad, casi siempre involucran violaciones de la regla # 1 también (con la que nadie discute). Nuevamente, desde el punto de vista de las herramientas automáticas, no es difícil hacer una herramienta que se queje inmediatamente cuando se viola la regla # 1, por lo que la mayoría de los errores se pueden detectar de manera oportuna.
El tercer detalle (que a menudo es olvidado por los oponentes de esa regla) es la naturaleza confusa de la declaración vacía que está representada por un solo punto y coma. La mayoría de los desarrolladores con algo de experiencia se confundieron, tarde o temprano, por un punto y coma incorrecto único o por una declaración vacía que se escribe utilizando el punto y coma único. Visualmente son más fáciles de detectar visualmente dos llaves en lugar de un solo punto y coma.
Tengo que admitir que no siempre se utiliza {}
para una sola línea, pero es una buena práctica.
Digamos que escribes un código sin corchetes que se ve así:
para (int i = 0; i <100; ++ i) para (int j = 0; j <100; ++ j) DoSingleStuff ();
Y después de algún tiempo, usted desea agregar en j
bucle algunas otras cosas y simplemente hacer eso por alineación y olvidarse de agregar paréntesis.
La reparto de memoria es más rápida. Digamos que tienes un gran alcance y crea grandes arreglos en el interior (sin
new
que estén apilados). Esas matrices se están eliminando de la memoria justo después de dejar el ámbito. Pero es posible que utilices esa matriz en un solo lugar y estará en la pila por un tiempo y sea una especie de basura. Como una pila tiene un tamaño limitado y bastante pequeño, es posible superar el tamaño de la pila. Así que en algunos casos es mejor escribir{}
para evitar eso. NOTA: esto no es para una sola línea sino para tales situaciones:if (...) {// SomeStuff ... {// no tenemos if, while, etc. // SomeOtherStuff} // SomeMoreStuff}
La tercera forma de usarlo es similar con la segunda. Simplemente no para hacer la pila más limpia sino para abrir algunas funciones. Si usas
mutex
funciones largas, generalmente es mejor bloquear y desbloquear justo antes de acceder a los datos y luego de terminar de leer / escribir eso. NOTA de esta manera está utilizando si tiene alguna propiaclass
ostruct
conconstructor
ydestructor
para bloquear la memoria.Qué es más:
if (...) if (...) SomeStuff (); de lo contrario SomeOtherStuff (); // va a la segunda si, pero alligment muestra que está en primera ...
En general, no puedo decir cuál es la mejor manera de usarlo siempre {}
para una sola línea, pero no es nada malo hacer eso.
EDICIÓN IMPORTANTE Si escribe compilaciones de código de compilación para una sola línea no hace nada, pero si su código se interpretará, ralentiza el código muy ligeramente. Muy ligeramente.
Una opción para ayudar a prevenir los errores que se han descrito anteriormente es alinear lo que quiere que suceda cuando no usa llaves. Hace que sea mucho más difícil no notar los errores cuando intenta modificar el código.
if (condition) doSomething();
else doSomethingElse();
if (condition) doSomething();
doSomething2(); // Looks pretty obviously wrong
else // doSomethingElse(); also looks pretty obviously wrong
wrt 6: Es más seguro porque eliminar un puntero nulo no es una opción. Por lo tanto, si por casualidad recorres esa ruta dos veces, no causarás que la corrupción de la memoria libere la memoria que sea gratuita o que haya sido asignada a otra cosa.
Esto es la mayoría de los problemas con objetos de alcance de archivos estáticos y singletons que no tienen tiempos de vida muy claros y se sabe que se recrean después de haber sido destruidos.
En la mayoría de los casos, puede evitar la necesidad de esto usando auto_ptrs