Categories
General

Atributos de clase constantes en C++

La inicialización de atributos estáticos constantes en C++ depende del tipo de constante que queramos crear.

Veamos que ocurre con el siguiente ejemplo (en comentario aparecen los números de línea para posterior referencia):

class Ejemplo {
  public:
   static const int x=10; // 10
   static const float PI =3.141592; // 11
   static const string s1="HOLA"; // 12
   static const string s2=string("HOLA"); // 13
   static const Ejemplo ejemplo1; // 14
   static const Ejemplo ejemplo2=Ejemplo(2); // 15
   Ejemplo(int x=0) : val(x) {}
private:
   int val;
};

Al intentar compilar esta declaración de clase con gcc (versión 4.3.3), aparecen estos errores:

atrib_constante.h:12: error: inicialización en la clase inválida para el miembro de datos static de tipo ‘const std::string’ que no es integral
atrib_constante.h:13: error: a call to a constructor no puede aparece en una expresión constante
atrib_constante.h:13: error: inicialización en la clase inválida para el miembro de datos static de tipo ‘const std::string’ que no es integral

que viene a decir que no se pueden inicializar constantes que no sean de tipo primitivo de C++ en la declaración de la clase. En la línea 15 el error de compilación es diferente:

atrib_constante.h:15: error: invalid use of incomplete type ‘class Ejemplo’
atrib_constante.h:8: error: forward declaration of ‘class Ejemplo’
atrib_constante.h:15: error: inicialización en la clase inválida para el miembro de datos static de tipo ‘const Ejemplo’ que no es integral

es decir, no podemos intentar crear en ese momento un objeto de clase Ejemplo, ya que ésta aún no está completamente definida. Sin embargo, sí podemos declarar objetos de clase (estáticos) de tipo Ejemplo dentro de la propia clase (línea 14), siempre y cuando no intentemos inicializarlos (construirlos) ahí. La solución a estos errores es simplemente realizar la inicialización en el fichero de implementación (o a continuación de la declaración de la clase):

class Ejemplo {
public:
 static const int x=10;
 static const float PI =3.141592;
 static const string s1;
 static const string s2;
 static const Ejemplo ejemplo1;
 static const Ejemplo ejemplo2;
 Ejemplo(int x=0) : val(x) {}
private:
 int val;
};

const string Ejemplo::s1 = "HOLA";
const string Ejemplo::s2 = string("HOLA");
const Ejemplo Ejemplo::ejemplo1;
const Ejemplo Ejemplo::ejemplo2 = Ejemplo(2);

En estas inicializaciones hay que tener en cuenta dos cosas:

  1. Hay que indicar el ámbito de las constantes, en este caso, la clase Ejemplo.
  2. No hay que poner el modificador ‘static’.

Fijémonos en ‘ejemplo1’. La inicialización no especifica a qué valor debe inicializarse el objeto. ¿Es por tanto necesaria? Si no lo hacemos obtendremos este bonito mensaje al intentar compilar y enlazar nuestro código para crear un programa ejecutable:

atrib_constante.cc:(.text+0x152): undefined reference to `Ejemplo::ejemplo1'

Es decir, ‘ejemplo1’ ha sido declarado pero no definido. Es necesario definirlo (construirlo) expresamente, mediante la instrucción de definición

const Ejemplo Ejemplo::ejemplo1;

Esto no es una declaración de ‘ejemplo1’ (ésta se hizo en la clase), sino su definición o, en otras palabras, su construcción. De hecho esa definición es equivalente a

const Ejemplo Ejemplo::ejemplo1=Ejemplo();

donde se ve explícitamente que al definir ‘ejemplo1’ estamos invocando al constructor por defecto de su clase.

By pierre

Coordinador de asignaturas de POO en los estudios de informática de la Escuela Politécnica Superior (Universidad de Alicante).