and c++ constants

and - C++ Mejores prácticas para constantes



const c++ (6)

Tengo un montón de constantes a las que quiero acceder en diferentes partes de mi código, pero a las que quiero tener acceso en general:

static const bool doX = true; static const bool doY = false; static const int maxNumX = 5;

etc.

Así que creé un archivo llamado "constantes.h", los pegué todos allí y lo #incluí en cualquier archivo que necesite saber una constante.

El problema es que esto es terrible para los tiempos de compilación, ya que cada vez que cambio una constante, todos los archivos que hacen referencia a constantes.h deben ser reconstruidos. (Además, como lo entiendo, ya que son estáticos, estoy generando una copia de doX / doY / maxNumX en el código cada vez que incluyo constantes.h en un nuevo .cpp, lo que lleva a kilobytes de espacio desperdiciado en el compilado. EXE - ¿Hay alguna manera de ver esto?).

Entonces, quiero una solución. Una que no es "declarar constantes solo en los archivos que las usan", si es posible.

¿Alguna sugerencia?


Creo que tu suposición de base está desactivada.

Sus otros encabezados generalmente se organizan manteniendo juntos lo que funciona en conjunto. Por ejemplo, una clase y sus métodos relacionados o dos clases fuertemente interconectadas.

¿Por qué agrupar todas las constantes en un solo encabezado? No tiene sentido. Es una idea tan mala como un "global.h" para incluir fácilmente cada dependencia individual.

En general, las constantes se utilizan en un contexto particular. Por ejemplo, una enumeración utilizada como indicador para una función en particular:

class File { public: enum class Mode { Read, Write, Append }; File(std::string const& filename, Mode mode); // ... };

En este caso, es natural que esas constantes vivan en el mismo encabezado que la clase a la que están vinculados (e incluso dentro de la clase).

La otra categoría de constantes son aquellas que solo impregnan toda la aplicación. Por ejemplo:

enum class Direction { Up, Down, Right, Left, Forward, Backward };

... en un juego en el que desea expresar el movimiento de los objetos con respecto a la dirección hacia la que están orientados.

En este caso, está bien crear un archivo de encabezado para este conjunto específico de constantes.

Y si realmente te preocupa agrupar esos archivos:

constants/ Direction.hpp Sandwich.hpp State.hpp

Y evitará el problema de volver a compilar toda la aplicación cuando agregue una constante ... aunque si lo necesita, hágalo, pagará el costo solo una vez, mejor que un diseño incorrecto que tendrá que Vive con el resto de tu trabajo.


El camino directo es, para crear símbolos no const:

const bool doX = true; const bool doY = false; const int maxNumX = 5;

Estos valores serán reemplazados por el compilador con los valores dados. Esa es la forma más eficiente. Por supuesto, esto también lleva a la recompilación tan pronto como modifica o agrega valores. Pero en la mayoría de los casos esto no debería plantear problemas prácticos.

Por supuesto que hay diferentes soluciones:

  • Al usar static const , (o miembros de la clase const estática), los valores pueden modificarse sin la compilación de todos los archivos a los que se hace referencia, pero de este modo los valores se mantienen en un segmento de datos const que se llamará durante el tiempo de ejecución en lugar de resolverse en tiempo de compilación. Si el rendimiento en tiempo de ejecución no es un problema (ya que lo es para el 90% del código más típico), está bien.

  • La forma directa de C ++ es utilizar enums clase en lugar de identificadores const globales (como se señaló mi Mathieu). Esto es más seguro para los tipos y, además, funciona de forma muy const : los símbolos se resolverán en el momento de la compilación.


La única alternativa es hacer que sus constantes sean extern y definirlas en otro archivo .cpp , pero perderá el potencial de optimización, porque el compilador no sabrá qué valor tienen al compilar cada .cpp`.

Por cierto, no se preocupe por el aumento de tamaño: para los tipos integrales, es probable que sus constantes estén en línea directamente en el código de máquina generado.

Finalmente, esa static no es necesaria, ya que, por defecto, las variables globales const son static en C ++.


Los declara como extern en el encabezado y los define en un archivo de implementación.

De esa manera, cuando desee cambiar su valor, modificará el archivo de implementación y no será necesaria una compilación completa.

El problema en su variante no está relacionado con la compilación, sino con la lógica. No serán globales, ya que cada unidad de traducción tendrá su propia copia de la variable.

EDITAR:

La forma de hacerlo de C ++ es realmente envolviéndolos en una clase:

//constants.h class Constants { public: static const bool doX; static const bool doY; static const int maxNumX; } //constants.cpp const bool Constants::doX = true; const bool Constants::doY = false; const int Constants::maxNumX = 5;


Otro enfoque que es mejor para los tiempos de compilación (pero tiene un costo de tiempo de ejecución menor) es hacer que las constantes sean accesibles a través de métodos estáticos en una clase.

//constants.h class Constants { public: static bool doX(); static bool doY(); static int maxNumX(); }; //constants.cpp bool Constants::doX() { return true; } bool Constants::doY() { return false; } int Constants::maxNumX() { return 42; }

La ventaja de este enfoque es que solo recompila todo si agrega / elimina / cambia la declaración de un método en el encabezado, mientras que cambiar el valor devuelto por cualquier método solo requiere compilar constantes.cpp (y vincularlo, por supuesto).

Como con la mayoría de las cosas, esto puede o no ser lo mejor en su caso particular, pero es otra opción a considerar.


¿Cuál es el problema con este uso?
No declare un tipo static en el archivo de cabecera, no hace lo que cree que hace.

Cuando declara una estática en el archivo de encabezado, se crea una copia de esa variable en cada Unidad de traducción (TU) donde se incluye ese archivo de encabezado, de modo que cada TU ve una variable diferente, esto es opuesto a su expectativa de tener un global.

Solución sugerida:
Debe declararlos como extern en un archivo de encabezado y definirlos en exactamente un archivo cpp, mientras que incluir el encabezado con extern en cada archivo cpp donde desee acceder a ellos.

Buena lectura:
¿Cómo debo usar extern?