Miembros estáticos

Hasta ahora, hemos considerado los campos y métodos de una clase que describen el estado y el comportamiento de los objetos de una clase determinada. Sin embargo, en los programas puede ser necesario almacenar ciertos atributos o realizar operaciones en toda la clase, en lugar de en sus objetos. Estas propiedades de clase se denominan estáticas y se describen utilizando la palabra clave static añadida antes del tipo. También se admiten en estructuras y uniones.

Por ejemplo, podemos contar el número de formas creadas por el usuario en un programa de dibujo. Para ello, en la clase Shape describiremos la variable estática count (Shapes5.mq5).

class Shape
{
private:
   static int count;
   
protected:
   ...
   Shape(int pxint pycolor backstring t) :
      coordinates(pxpy),
      backgroundColor(back),
      type(t)
   {
      ++count;
   }
   
public:
   ...
   static int getCount()
   {
      return count;
   }
};

Se define en la sección private y, por tanto, no es accesible desde el exterior.

Para leer el valor actual del contador se proporciona un método estático público getCount(). En teoría, dado que los miembros estáticos se definen en el contexto de una clase, reciben restricciones de visibilidad según el modificador de la sección en la que se encuentran.

Incrementaremos el contador en 1 en el constructor paramétrico Shape y eliminaremos el constructor por defecto. Así, se tendrá en cuenta cada instancia de una forma de cualquier tipo derivado.

Tenga en cuenta que una variable estática debe definirse explícitamente (y, opcionalmente, inicializarse) fuera del bloque de clase:

static int Shape::count = 0;

Las variables estáticas de clase son similares a las variables globales y a las variables estáticas dentro de funciones (véase la sección Variables estáticas) en el sentido de que se crean cuando se inicia el programa y se borran antes de que se descargue. Por lo tanto, a diferencia de las variables de objeto, deben existir desde el principio como una única instancia.

En este caso, la inicialización a cero puede omitirse porque, como sabemos, las variables globales y estáticas se ponen a cero por defecto. Las matrices también pueden ser estáticas.

En la definición de una variable estática vemos el uso del operador especial de selección de contexto '::'. Con él se forma un nombre de variable totalmente cualificado. A la izquierda de '::' está el nombre de la clase a la que pertenece la variable, y a la derecha su identificador. Obviamente, el nombre completamente cualificado es necesario, porque dentro de clases diferentes se pueden declarar variables estáticas con el mismo identificador, y se necesita una forma de referirse de forma única a cada una de ellas.

El mismo operador '::' se utiliza para acceder no sólo a las variables estáticas públicas de la clase, sino también a los métodos. En concreto, para llamar al método getCount en la función OnStart, utilizamos la sintaxis Shape::getCount():

void OnStart()
{
   for(int i = 0i < 10; ++i)
   {
      Shape *shape = addRandomShape();
      shape.draw();
      delete shape;
   }
   
   Print(Shape::getCount()); // 10
}

Como ahora se está generando el número de formas especificado (10), podemos comprobar que el contador funciona correctamente.

Si tiene un objeto de clase, puede hacer referencia a un método o propiedad estáticos mediante la desreferenciación habitual (por ejemplo, shape.getCount()), pero dicha notación puede ser engañosa (ya que oculta el hecho de que en realidad no se accede al objeto).

Tenga en cuenta que la creación de clases derivadas no afecta en modo alguno a las variables y métodos estáticos: estos siempre se asignan a la clase en la que se definieron. Nuestro contador es el mismo para todas las clases de formas derivadas de Shape.

No se puede utilizar this dentro de métodos estáticos porque se ejecutan sin estar vinculados a un objeto específico. Además, desde un método estático no se puede invocar directamente un método de clase normal o acceder a su campo sin desreferenciar ninguna variable de tipo objeto. Por ejemplo, si llama a draw desde getCount, obtendrá un error «acceso a función o miembro no estático»:

   static int getCount()
   {
      draw(); // error: 'draw' - access to non-static member or function
      return count;
   }

Por la misma razón, los métodos estáticos no pueden ser virtuales.

¿Es posible, utilizando variables estáticas, calcular, no el número total de formas, sino sus estadísticas por tipo? Sí, es posible. Esta tarea se deja para su estudio por separado. Los interesados pueden encontrar uno de los ejemplos de aplicación en el script Shapes5stats.mq5.