resolucion - ¿Dónde declarar/definir constantes de alcance de clase en C++?
operador de resolucion de ambito c++ (7)
Tengo curiosidad por los beneficios / detrimentos de las diferentes opciones de declaración constante y definición en C ++. Durante mucho tiempo, los he estado declarando en la parte superior del archivo de encabezado antes de la definición de clase:
//.h
const int MyConst = 10;
const string MyStrConst = "String";
class MyClass {
...
};
Si bien esto contamina el espacio de nombres global (que sé que es algo malo, pero nunca he encontrado una lista de razones por las que es malo), las constantes aún estarán sujetas a unidades de traducción individuales, por lo que los archivos que no incluyen este encabezado No tendrá acceso a estas constantes. Pero puede obtener colisiones de nombres si otras clases definen una constante del mismo nombre, lo que podría decirse que no es algo malo, ya que puede ser una buena indicación de un área que podría ser refaccionada.
Recientemente, decidí que sería mejor declarar constantes específicas de clase dentro de la propia definición de clase:
//.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ía dependiendo de si la constante se usa solo internamente para la clase o se necesita para otros objetos que la usan. Esto es lo que creo que es la mejor opción en este momento, principalmente porque puede mantener las constantes de clase internas 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 detrimento de requerir una inicialización no integral en el archivo cpp.
También he considerado mover las constantes a 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 la clase.
Solo buscaba opiniones y posiblemente otras opciones que no había considerado todavía.
Contaminar el espacio de nombres global debería ser evidentemente malo. Si incluyo un archivo de encabezado, no quiero encontrar o depurar las colisiones de nombres con constantes declaradas en ese encabezado. Estos tipos de errores son realmente frustrantes y, a veces, difíciles de diagnosticar. Por ejemplo, una vez tuve que enlazar con un proyecto que tenía esto definido en un encabezado:
#define read _read
Si sus constantes son contaminación del espacio de nombres, se trata de desechos nucleares del espacio de nombres. La manifestación de esto fue una serie de errores de compilación muy extraños quejándose de que faltaba 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 razón para que esto deba 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 a personas poner constantes en su propia clase, que se puede implementar como un singleton. Esto me parece trabajo sin recompensa, el lenguaje te proporciona algunas facilidades para declarar constantes.
La contaminación del espacio de nombres global es mala porque alguien (por ejemplo, el escritor de una biblioteca que usa) podría querer usar el nombre MyConst
para otro propósito. Esto puede llevar a problemas graves (bibliotecas que no pueden usarse 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 de espacio de nombres es mejor que eso. Por cierto: si debe ser compatible con 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 de C ++ o usar el truco de enum
en este caso.
Creo que no hay mejores opciones para las constantes, al menos no puedo pensar en una en este momento ...
Personalmente uso tu segundo enfoque; Lo he usado durante años, y me funciona bien.
Desde un punto de visibilidad, tendería a hacer que las constantes privadas sean estadísticas de nivel de archivo, ya que nadie fuera del archivo de implementación necesita saber que existen; esto ayuda a evitar que las reacciones en cadena se vuelvan a compilar si necesita cambiar sus nombres o agregar nombres nuevos, ya que su ámbito de nombre es el mismo que el de uso ...
Puede declararlos como globales en el archivo c ++, siempre que no estén referenciados en el encabezado. Entonces son privados para esa clase y no contaminarán el espacio de nombres global.
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 va a utilizar las constantes, declare dentro de una clase / estructura que solo contenga 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 partes sustanciales de la misma), declararlas dentro de un espacio de nombres en un encabezado que esté (implícita o explícitamente) incluido en todas partes.
namespace MyAppGlobalConstants
{
//declare constants here
}
Su afirmación de que declarar una constante no integral como miembro de clase estática "tiene el detrimento de requerir una inicialización no integral en el archivo cpp" no es exactamente sólida, por así decirlo. Requiere una definición en el archivo cpp, pero no es un "detrimento", es una cuestión de su intención. El objeto const
nivel de espacio de nombres en C ++ tiene un enlace interno por defecto, lo que significa que en su variante original la declaración
const string MyStrConst = "String";
es equivalente a
static const string MyStrConst = "String";
es decir, definirá un objeto MyStrConst
independiente en cada unidad de traducción en la que se incluye este archivo de encabezado. ¿Estás consciente de esto? ¿Fue esta tu intención o no?
En cualquier caso, si no necesita específicamente un objeto separado en cada unidad de traducción, la declaración de constante de MyStrConst
en su ejemplo original no es una buena práctica. Normalmente, solo pondrías una declaración no definitoria en el archivo de encabezado
extern const string MyStrConst;
y proporcionar una definición en el archivo cpp
const string MyStrConst = "String";
asegurándose así de 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. Por lo tanto, independientemente de cómo lo declare (dentro o fuera de la clase), normalmente siempre tendrá que lidiar con el "detrimento" de tener que definirlo en un archivo cpp. Por supuesto, como dije anteriormente, con las constantes de espacio de nombres puedes salirte con lo que tienes en tu primera variante, pero eso sería solo un ejemplo de "codificación perezosa".
De todos modos, no creo que haya una razón para complicar en exceso el problema: si la constante tiene un "vínculo" obvio a la clase, debe declararse como miembro de la clase.
Los especificadores de acceso PS ( public
, protected
, private
) no controlan la visibilidad del nombre. Solo controlan su accesibilidad . El nombre permanece visible en cualquier caso.
no contamine el espacio de nombres global, contamine local.
namespace Space
{
const int Pint;
class Class {};
};
Pero prácticamente ...
class Class
{
static int Bar() {return 357;}
};