2010-01-11 21 views
45

Tengo curiosidad acerca de los beneficios/detrimentos de diferentes declaraciones constantes y opciones de definición en C++. Durante mucho tiempo, me acaban de declararlos en la parte superior del archivo de cabecera antes de la definición de clase:¿Dónde declarar/definir constantes de alcance de clase en C++?

//.h 
const int MyConst = 10; 
const string MyStrConst = "String"; 
class MyClass { 
... 
}; 

Si bien esto contamina el espacio de nombres global (que sé que es una mala cosa, pero nunca he encontrado una larga lista de motivos por los que es malo), las constantes seguirán siendo delimitadas en unidades de traducción individuales, por lo que los archivos que no incluyen este encabezado no tendrán acceso a estas constantes. Pero puede obtener colisiones de nombres si otras clases definen una constante del mismo nombre, lo que posiblemente no es algo malo, ya que puede ser una buena indicación de un área que podría ser refactorizada.

Recientemente, decidí que sería mejor declarar constantes específicas de clase dentro de la definición de clase en sí:

//.h 
class MyClass { 
    public: 
     static const int MyConst = 10; 
... 
    private: 
     static const string MyStrConst; 
... 
}; 
//.cpp 
const string MyClass::MyStrConst = "String"; 

La visibilidad de la constante se ajustará en función de si la constante sólo se utiliza internamente para la clase o es necesaria para otros objetos que usan la clase. Esto es lo que estoy pensando que es la mejor opción en este momento, principalmente porque puede mantener las constantes internas de clase privadas para la clase y cualquier otra clase que use las constantes públicas tendría una referencia más detallada a la fuente de la constante (por ejemplo, MyClass: : MyConst). Tampoco contaminará el espacio de nombres global. Aunque tiene el inconveniente de requerir una inicialización no integral en el archivo cpp.

También consideré mover las constantes en su propio archivo de encabezado y envolverlas en un espacio de nombres en caso de que alguna otra clase necesite las constantes, pero no toda la definición de clase.

Estoy buscando opiniones y posiblemente otras opciones que no había considerado todavía.

Respuesta

1

Puede declararlos como globales en el archivo C++, siempre que no se mencionen en el encabezado. Entonces son privados para esa clase y no contaminarán el espacio de nombres global.

+0

Sí, pero hay que usar 'static' o un espacio de nombres en el anonimato, en este caso - si no lo hace, en algunas implementaciones que contaminan el espacio de nombres global utilizado por el enlazador y obtener conflictos de nombres en la vinculación ... – hjhill

9

La contaminación del espacio de nombres global es mala porque alguien (por ejemplo, el escritor de una biblioteca que utiliza) podría querer usar el nombre MyConst para otro fin. Esto puede ocasionar problemas graves (bibliotecas que no se pueden usar juntas, etc.)

Su segunda solución es claramente la mejor si las constantes están vinculadas a una sola clase. Si eso no es tan fácil (piense en constantes físicas o matemáticas sin vínculos con una clase en su programa), la solución del espacio de nombres es mejor que eso. Por cierto: si debe ser compatible con los compiladores de C++ anteriores, recuerde que algunos de ellos no pueden usar la inicialización integral en un archivo de encabezado; debe inicializar en el archivo C++ o usar el antiguo truco enum en este caso.

Creo que hay mejores opciones para las constantes - al menos no se puede pensar en uno en el momento ...

2

Personalmente utilizo el segundo enfoque; Lo he usado durante años, y funciona bien para mí.

Desde un punto de visibilidad tendería a hacer que las constantes privadas se mantuvieran estáticas a nivel de archivo, ya que nadie fuera del archivo de implementación necesita saber que existen; esto ayuda a prevenir recompilaciones en cadena si necesita cambiar sus nombres o agregar nuevos ya que el alcance de su nombre es el mismo que su alcance de uso ...

35

Su afirmación de que se declara una constante no integral como miembro de clase estática " tener el detrimento de requerir inicialización no integral en el archivo cpp "no es exactamente sólido, por así decirlo.Requiere una definición en el archivo cpp, pero no es un "detrimento", es una cuestión de su intención. A nivel de espacio de nombres const objeto en C++ tiene enlace interno por defecto, lo que significa que en su variante original de la declaración

const string MyStrConst = "String"; 

es equivalente a

static const string MyStrConst = "String"; 

es decir, se definirá un MyStrConst objeto independiente en cada unidad de traducción en el que se incluye este archivo de encabezado. ¿Estás consciente de esto? ¿Fue esta tu intención o no?

En cualquier caso, si no lo hace específicamente necesitan un objeto independiente en cada unidad de traducción, la declaración de MyStrConst constante en su ejemplo original no es una buena práctica. Normalmente, sólo se había puesto una declaración no definir en el archivo de cabecera

extern const string MyStrConst; 

y proporcionar una definición en el archivo CPP

const string MyStrConst = "String"; 

asegurando así que todo el programa utiliza el mismo objeto constante . En otras palabras, cuando se trata de constantes no integrales, una práctica normal es definirlas en un archivo cpp. Entonces, independientemente de cómo lo declare (en clase o fuera), siempre tendrá que lidiar con el "detrimento" de tener que definirlo en un archivo cpp. Por supuesto, como dije antes, con las constantes del espacio de nombres puede salirse con la suya en su primera variante, pero eso sería solo un ejemplo de "codificación diferida".

De todos modos, no creo que haya una razón para complicar demasiado el problema: si la constante tiene un "adjunto" obvio a la clase, debe declararse como miembro de la clase.

P.S. Los especificadores de acceso (public, protected, private) no controlan visibilidad del nombre. Solo controlan su accesibilidad. El nombre permanece visible en cualquier caso.

+0

¿Puedo preguntar cuál es la diferencia entre visibilidad y accesibilidad? Creo que son lo mismo, ¿puedes dar un ejemplo? – toolchainX

+0

@toolchainX: Ejemplo: una función miembro puede ser privada, pero la función puede ser 'visible' para el compilador en el sentido de que suponemos que la definición existe y el compilador puede 'verla'. El compilador simplemente no impone el acceso [Error del compilador]. Eliminar la definición o colocar la definición en algún lugar "oculto" haría que la función en estos términos no sea "visible", ni siquiera para las funciones miembro, independientemente del acceso [Error de enlace]. – wardw

+0

Sería en detrimento colocarlo en el archivo .cpp porque el compilador no puede alinear la constante porque no está en la unidad de traducción del compilador. Si la constante es solo una clase que envuelve un entero, entonces sería mejor tenerla en el archivo de encabezado para que el compilador sepa cuál es su valor en todas las unidades de compilación. – qbt937

5

La contaminación del espacio de nombres global debería ser evidentemente mala. Si incluyo un archivo de encabezado, no quiero encontrar ni depurar colisiones de nombres con las constantes declaradas en ese encabezado. Este tipo de errores son realmente frustrantes y a veces difíciles de diagnosticar. Por ejemplo, una vez tuve a enlazar con un proyecto que había esta definido en un encabezado:

#define read _read 

Si sus constantes son la contaminación del espacio de nombres, se trata de residuos nucleares espacio de nombres. La manifestación de esto fue una serie de errores de compilador muy extraños quejándose de la pérdida de la función _read, pero solo cuando se vinculaba con esa biblioteca. Eventualmente cambiamos el nombre de las funciones de lectura a otra cosa, lo cual no es difícil, pero debería ser innecesario.

Su segunda solución es muy razonable ya que pone la variable dentro del alcance. No hay ninguna razón para que esto tenga que asociarse con una clase, y si necesito compartir constantes entre clases, declararé constantes en su propio espacio de nombres y archivo de encabezado. Esto no es bueno para el tiempo de compilación, pero a veces es necesario.

También he visto personas poner constantes en su propia clase, que se puede implementar como singleton. Esto para mí parece funcionar sin recompensa, el lenguaje te proporciona algunas facilidades para declarar constantes.

2

Si solo una clase va a usar estas constantes, declararlas como static const dentro del cuerpo de la clase. Si un grupo de clases relacionadas van a usar las constantes, declarelas dentro de una clase/estructura que solo contiene las constantes y los métodos de utilidad o dentro de un espacio de nombres dedicado. Por ejemplo,

namespace MyAppAudioConstants 
{ 
    //declare constants here 
} 

Si son constantes utilizadas por toda la aplicación (o trozos sustanciales de la misma), declaran ellos dentro de un espacio de nombres en un encabezado que es (implícita o explícitamente) que se incluye en todas partes.

namespace MyAppGlobalConstants 
{ 
    //declare constants here 
} 
1

no contaminan el espacio de nombres global, contaminan el local.

namespace Space 
    { 
    const int Pint; 
    class Class {}; 
    }; 

Pero en la práctica ...

class Class 
    { 
    static int Bar() {return 357;} 
    };