2012-03-10 21 views
16

que tengo un montón de constantes que quiero acceso en diferentes partes de mi código, pero que quieren tener fácil acceso a su conjunto:mejores prácticas de C++ para las constantes

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

etc.

Así que creé un archivo llamado "constants.h" y los puse todos allí y #incluido en cualquier archivo que necesita conocer 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 constantes.h de referencia deben ser reconstruidos. (Además, tal como lo entiendo, dado 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 forma de ver esto?).

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

¿Alguna sugerencia?

+3

En primer lugar: OH NOES, A FEW KILOBYTES !! En segundo lugar: es casi seguro que el compilador los dividirá en una constante, incluso si no se elimina por completo del ejecutable final. – Puppy

+0

¿Por qué asumes que hay una copia física? A menos que haya una necesidad de una ubicación física (como tomar la dirección de la variable), entonces el compilador puede muy felizmente optimizar eso. –

+0

El compilador necesita saber el valor de cada constante durante el tiempo de compilación, si desea obtenerlos doblados – hirschhornsalz

Respuesta

2

¿Cuál es el problema con este uso?
No declarar un tipo static en el archivo de encabezado. No hace lo que crees que hace.

Cuando se declara una estática en el encabezado del archivo una copia de esa variable se crea en cada Translation Unit(TU), donde se incluye el archivo de cabecera, por lo que cada TU ve una variable diferente, esto es contrario a la expectativa de tener un mundial.

Solución sugerida:
Usted debe declarar como extern en un fichero de cabecera y definirlos en exactamente un archivo CPP, mientras que incluyen la cabecera con extern en cada archivo de CPP en la que desea tener acceso a ellos.

Buena lectura:
How should i use extern?

+0

No creo que 'static' sea un problema para las constantes ... –

+2

En esta situación (porque son const) usaría static (con eso quiero decir que no les declararía nada simplemente no los haría extern (por lo tanto predeterminado a estático)). Como proporciona una mejor oportunidad para que un compilador optimice completamente el valor. –

+0

'const' a nivel global y de nivel de espacio de nombres implica vinculación interna, es decir' const estático' es lo mismo que 'const', pero no' const extern '. Este comportamiento se implementó específicamente en C++ para evitar el uso de macros para las constantes. El compilador no pondrá esa constante en la sección .data del archivo de objeto si no se toma la dirección de esa constante. Su valor será codificado, ya que es esencialmente una constante de tiempo de compilación. –

6

se declaran como extern en la cabecera y definir en un archivo de aplicación.

De esta forma, cuando desee cambiar su valor, modifique el archivo de implementación y no es necesaria una compilación completa.

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

EDIT:

El C++ - forma ish de hacer lo que realmente les envolvía 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; 
+0

No serán expresiones constantes si esto se hace, lo cual es problemático. – Puppy

+0

@DeadMG No sigo. –

+8

Diría que es el enfoque Java-ish. –

9

La única alternativa es hacer que sus constantes extern y definirlos en otro archivo .cpp, pero perderá potencial para la 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 alineadas directamente en el código de máquina generado.

Por último, ese static no es necesario, ya que por defecto const las variables globales son static en C++.

+1

La optimización del tiempo de enlace se ocupará de esto. – Puppy

+0

Una variable extern 'const' de un tipo integral ya no es una expresión en tiempo de compilación, por lo que ya no se puede usar para límites de matriz, en sentencias' case', etc. –

1

Otro enfoque que es mejor para los tiempos de compilación (pero tiene un costo menor de tiempo de ejecución) 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 sólo se vuelva a compilar todo si añadir/quitar/cambiar la declaración de un método en la cabecera, mientras se cambia el valor devuelto por cualquier método sólo requiere la compilación de constants.cpp (y vinculación, por supuesto).

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

5

Creo que su 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 encabezado "global.h" para incluir todas las dependencias fácilmente.

En general, las constantes se usan en un contexto particular. Por ejemplo, una enumeración usa como una bandera para una función 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 viven en el mismo encabezado que la clase que están obligados a (e incluso dentro de la clase)

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

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

... en un juego en el que desea expresar mover objetos con respecto a la dirección que se enfrentan.

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

Y si realmente está preocupado por la agrupación de los archivos juntos:

constants/ 
    Direction.hpp 
    Sandwich.hpp 
    State.hpp 

Y usted perfectamente eludir la cuestión de recompilar la aplicación cuando se agrega una constante ... aunque si es necesario, hacer esto, usted está pagando el costo una sola vez, mejor que un diseño equivocado con el que tendrá que vivir por el resto de su trabajo.

+1

+1 Al agregar/cambiar una constante lleva a casi todos los otros archivos de origen que necesitan ser recompilados, llamo a ese fenómeno "recompilar el mundo". Alguien más probablemente ya haya inventado esa expresión. –

0

La forma recta hacia adelante 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. Esto también conduce, por supuesto, a la recompilación tan pronto como modifique o agregue valores. Pero en la mayoría de los casos esto no debería plantear problemas prácticos.

Por supuesto hay soluciones diferentes:

  • Uso static const s, (o miembros de la clase const estáticos) los valores pueden ser modificados sin recompilación de todos los ficheros referidos - pero así los valores se mantienen en una constante segmento de datos que se llamará durante el tiempo de ejecución en lugar de resolverse en compilación. Si el rendimiento del tiempo de ejecución no es un problema (como lo es para el 90% del código más típico), eso está bien.

  • La forma recta de C++ está utilizando la clase enums en lugar de los identificadores globales const (como noté mi Mathieu). Esto es más seguro y además funciona igual que const: los símbolos se resolverán en tiempo de compilación.

Cuestiones relacionadas