Pregunta 1: ¿Declarar una variable dentro de un ciclo es una buena práctica o una mala práctica?
He leído los otros hilos sobre si existe o no un problema de rendimiento (la mayoría dijo que no), y que siempre debe declarar las variables tan cerca de donde se van a utilizar. Lo que me pregunto es si esto debería evitarse o no, o si realmente es preferible.
Ejemplo:
for(int counter = 0; counter <= 10; counter++)
{
string someString = "testing";
cout << someString;
}
Pregunta # 2: ¿La mayoría de los compiladores se dan cuenta de que la variable ya ha sido declarada y simplemente omiten esa parte, o cada vez crea un lugar para ella en la memoria?
c++
loops
variable-declaration
JeramyRR
fuente
fuente
Respuestas:
Esta es una excelente práctica.
Al crear variables dentro de los bucles, se asegura de que su alcance esté restringido al interior del bucle. No se puede hacer referencia ni llamar fuera del bucle.
De esta manera:
Si el nombre de la variable es un poco "genérico" (como "i"), no hay riesgo de mezclarlo con otra variable del mismo nombre más adelante en su código (también puede mitigarse usando las
-Wshadow
instrucciones de advertencia en GCC)El compilador sabe que el alcance de la variable está limitado al interior del bucle y, por lo tanto, emitirá un mensaje de error adecuado si la variable se menciona por error en otro lugar.
Por último, pero no menos importante, el compilador puede realizar una optimización dedicada de manera más eficiente (lo más importante es la asignación de registros), ya que sabe que la variable no se puede usar fuera del ciclo. Por ejemplo, no es necesario almacenar el resultado para su reutilización posterior.
En resumen, tienes razón en hacerlo.
Sin embargo, tenga en cuenta que no se supone que la variable retenga su valor entre cada ciclo. En tal caso, es posible que deba inicializarlo cada vez. También puede crear un bloque más grande, que abarque el ciclo, cuyo único propósito es declarar variables que deben retener su valor de un ciclo a otro. Esto generalmente incluye el contador de bucle en sí.
Para la pregunta # 2: la variable se asigna una vez, cuando se llama a la función. De hecho, desde una perspectiva de asignación, es (casi) lo mismo que declarar la variable al comienzo de la función. La única diferencia es el alcance: la variable no se puede usar fuera del bucle. Incluso puede ser posible que la variable no esté asignada, simplemente reutilizando algún espacio libre (de otra variable cuyo alcance ha finalizado).
Con un alcance restringido y más preciso vienen optimizaciones más precisas. Pero lo que es más importante, hace que su código sea más seguro, con menos estados (es decir, variables) de los que preocuparse al leer otras partes del código.
Esto es cierto incluso fuera de un
if(){...}
bloque. Por lo general, en lugar de:es más seguro escribir:
La diferencia puede parecer menor, especialmente en un ejemplo tan pequeño. Pero en una base de código más grande, ayudará: ahora no hay riesgo de transportar algún
result
valor desdef1()
unf2()
bloque. Cada unoresult
está estrictamente limitado a su propio alcance, lo que hace que su función sea más precisa. Desde la perspectiva del crítico, es mucho mejor, ya que tiene menos variables de estado de largo alcance de las que preocuparse y rastrear.Incluso el compilador ayudará mejor: suponiendo que, en el futuro, después de algún cambio erróneo de código,
result
no se inicialice correctamentef2()
. La segunda versión simplemente se negará a funcionar, indicando un mensaje de error claro en tiempo de compilación (mucho mejor que el tiempo de ejecución). La primera versión no detectará nada, el resultadof1()
simplemente se probará por segunda vez, confundiéndose con el resultado def2()
.Información complementaria
La herramienta de código abierto CppCheck (una herramienta de análisis estático para código C / C ++) proporciona algunos consejos excelentes sobre el alcance óptimo de las variables.
En respuesta al comentario sobre la asignación: la regla anterior es verdadera en C, pero podría no serlo para algunas clases de C ++.
Para los tipos y estructuras estándar, el tamaño de la variable se conoce en el momento de la compilación. No hay tal cosa como "construcción" en C, por lo que el espacio para la variable simplemente se asignará a la pila (sin ninguna inicialización), cuando se llama a la función. Es por eso que hay un costo "cero" al declarar la variable dentro de un bucle.
Sin embargo, para las clases de C ++, existe esta cosa de constructor de la que sé mucho menos. Supongo que la asignación probablemente no será el problema, ya que el compilador será lo suficientemente inteligente como para reutilizar el mismo espacio, pero es probable que la inicialización tenga lugar en cada iteración del bucle.
fuente
string
yvector
específicamente, el operador de asignación puede reutilizar el búfer asignado a cada ciclo, lo que (dependiendo de su ciclo) puede ser un gran ahorro de tiempo.En general, es una muy buena práctica mantenerlo muy cerca.
En algunos casos, habrá una consideración como el rendimiento que justifica sacar la variable del bucle.
En su ejemplo, el programa crea y destruye la cadena cada vez. Algunas bibliotecas utilizan una optimización de cadena pequeña (SSO), por lo que la asignación dinámica podría evitarse en algunos casos.
Supongamos que desea evitar esas creaciones / asignaciones redundantes, lo escribiría como:
o puedes sacar la constante:
Puede reutilizar el espacio que consume la variable y puede extraer invariantes de su ciclo. En el caso de la matriz const char (arriba), esa matriz podría extraerse. Sin embargo, el constructor y el destructor deben ejecutarse en cada iteración en el caso de un objeto (como
std::string
). En el caso destd::string
, ese 'espacio' incluye un puntero que contiene la asignación dinámica que representa los caracteres. Así que esto:requeriría una copia redundante en cada caso, y una asignación dinámica y libre si la variable se ubica por encima del umbral para el recuento de caracteres SSO (y su biblioteca estándar implementa SSO).
Haciendo esto:
aún requeriría una copia física de los caracteres en cada iteración, pero el formulario podría dar como resultado una asignación dinámica porque asigna la cadena y la implementación debería ver que no es necesario cambiar el tamaño de la asignación de respaldo de la cadena. Por supuesto, no haría eso en este ejemplo (porque ya se han demostrado múltiples alternativas superiores), pero podría considerarlo cuando varía el contenido de la cadena o del vector.
Entonces, ¿qué haces con todas esas opciones (y más)? Manténgalo muy cerca por defecto, hasta que comprenda bien los costos y sepa cuándo debe desviarse.
fuente
Para C ++ depende de lo que esté haciendo. OK, es un código estúpido pero imagina
Esperará 55 segundos hasta que obtenga la salida de myFunc. Solo porque cada contructor de bucle y destructor juntos necesitan 5 segundos para terminar.
Necesitará 5 segundos hasta que obtenga la salida de myOtherFunc.
Por supuesto, este es un ejemplo loco.
Pero ilustra que podría convertirse en un problema de rendimiento cuando cada ciclo se realiza la misma construcción cuando el constructor y / o destructor necesita algo de tiempo.
fuente
No publiqué para responder las preguntas de JeremyRR (como ya han sido respondidas); en cambio, publiqué simplemente para dar una sugerencia.
Para JeremyRR, podrías hacer esto:
No sé si se da cuenta (no lo hice cuando comencé a programar), que los corchetes (siempre que estén en pares) se pueden colocar en cualquier lugar dentro del código, no solo después de "if", "for", " mientras ", etc.
Mi código compilado en Microsoft Visual C ++ 2010 Express, así que sé que funciona; Además, intenté usar la variable fuera de los corchetes en los que se definió y recibí un error, por lo que sé que la variable fue "destruida".
No sé si es una mala práctica usar este método, ya que muchos paréntesis sin etiquetar podrían hacer que el código sea ilegible rápidamente, pero tal vez algunos comentarios podrían aclarar las cosas.
fuente
Es una muy buena práctica, ya que todas las respuestas anteriores brindan un aspecto teórico muy bueno de la pregunta, déjenme echar un vistazo al código, estaba tratando de resolver DFS sobre GEEKSFORGEEKS, encuentro el problema de optimización ...... Si intenta resolver el código que declara el número entero fuera del ciclo le dará un error de optimización.
Ahora ponga números enteros dentro del ciclo, esto le dará la respuesta correcta ...
esto refleja completamente lo que dijo sir @justin en el segundo comentario ... intente esto aquí https://practice.geeksforgeeks.org/problems/depth-first-traversal-for-a-graph/1 . solo inténtalo ... lo conseguirás. Espero esta ayuda.
fuente
flag
debe reinicializarse en 0 cadawhile
iteración. Ese es un problema de lógica, no un problema de definición.Capítulo 4.8 Estructura de bloques en el lenguaje de programación C de K&R 2.Ed. :
Podría haber extrañado ver la descripción relevante en el libro como:
Pero una prueba simple puede probar la suposición sostenida:
fuente