Introducción

Igual como la palabra clave const, el modificador static trae confusiones porque se puede utilizar en varios contextos en C y C++. Podemos declarar estáticas funciones y métodos. Variables pueden estar estáticas dentro de funciones, métodos, clases y a nivel global, es decir, por todas partes.

Variables estáticas locales

Lo más común es declarar una variable local estática:

void mi_funcion_o_metodo(void)
{
    static int var_estatica = 4;
}

El valor de la variable var_estatica no se pierde aunque ya no se encuentre dentro del alcance, es decir, cuando el programa haya salido de la función en que la variable fue definida. Esto diferencia una variable estática de una variable no estática, cuyo valor se pierde al final de la función.

Debido a esta diferencia, las variables estaticas no se guardan en el stack sino en una memoria especial para variables estáticas. Aunque no sean accesibles durante toda la ejecución del programa, las variables estáticas existen durante toda la ejecución del programa.

Igual como en el caso de las variables convencionales, se llama al constructor de las variables estáticas cuando se construyen. Esto succede una vez antes de llamar a la función main. Igualmente se llama una vez al destructor después de salir del programa. Recuerda que las variables estáticas existen durante toda la ejecución del programa. Por eso deben inicalizarse antes de entrar y después de salir de la función principal.

No hay diferencia entre variables estáticas en funciones y métodos de clases. De hecho, una variable estática se puede definir dentro de cualquier bloque delimitado por {}.

Miembros estáticos de una clase

Variables miembros de una clase también pueden ser estáticas. Una variable estática de una clase es un dato común a todas las instancias de esta clase y accesible en todos los métodos. Una variable miembro estática es incluso accesible sin instancia alguna.

En el código siguiente definimos una clase con una variable miembro estática:

class MiClase
{
public:
    static int mi_variable;
};

Esta variable se inicializa así:

int MiClase::mi_variable = 5;

Si la queremos usar sin instancia, entonces usamos el nombre cualificado:

void haz_algo(void)
{
    MiClase::miVariable = 3;
}

Lógicamente, la variable miembro debe ser pública para que la podamos usar fuera de los métodos de la clase. (Dentro de los métodos de la clase no hace falta poner el nombre de la clase delante.)

Las variables miembros estáticas existen también durante toda la ejecución del programa. Su diferencia con las variables estáticas definidas en una función o método está en su alcance: Las variables miembros están al alcance de todos los métodos de una clase y no sólo de uno.

Constantes estáticas

Variables estáticas se pueden declarar también const. Una constante estática es una constante universal del programa, pero de alcance limitado. Es una buena práctica utilizar constantes estáticas en lugar de macros #define ya que las constantes llevan un tipo: un 5 puede ser un int, un char o un float pero un dato

const static int var = 5;

siempre es un int.

Métodos estáticos

Métodos de clases también pueden ser estáticas. Declarar un método estático es una promesa que este método sólo utiliza miembros estáticos de la clase. Este tipo de método no conoce el puntero this, por lo cual no se puede hacer directamente referencia a métodos y variables no estátitocs de la clase.

class MiClase
{
public:
    // Una variable estática
    static int una_variable_estatica;

    // Una variable no estática
    int una_variable_no_estatica;

    // Un método estático
    static void haz_algo(void)
    {
        // Se pueden usar variables estáticas
        una_variable_estatica = 5;

        // Error: no se pueden usar variables que
        // requieren un puntero this
        una_variable_no_estatica = 5;  // Error de compilación
    }

    // Un método normal (no estático)
    void haz_otra_cosa(void)
    {
        // Se pueden usar variables y métodos estáticos
        // también en métodos no estáticos
        una_variable_estatica = 5;
        haz_algo();

        // Una variable de instancia en un método de instancia
        una_variable_no_estatica = 5;  // Correcto
    }
};

Es posible llamar a métodos estáticos de una clase sin tener una instancia de ella utilizando el nombre cualificado.

void una_funcion_cualquiera(void)
{
    // Correcto: Se puede llamar a un método no estático
    // si instancia
    MiClase::haz_algo();

    // Sin embargo, también se le puede llamar con instancia
    MiClase mi_instancia;
    mi_instancia.haz_algo();

    // No es posible llamar a un método convencional
    // sin instancia
    MiClase::haz_otra_cosa();  // Error de compilación
}

Métodos estáticos son similares a funciones globales. Sólo llevan el nombre de la clase como prefijo. (Un nombre de espacio no se usa de forma diferente que una clase con todos los miembros públicos y estáticos.)

Métodos estáticos pueden sobrecargar los nombres métodos no estático. De hecho es una práctica bastante común – sobre todo para métodos recursivos. Como ejemplo podemos considerar una clase String, en que un método convencional afecta a la instancia de una clase, mientras el método estático a un parámetro.

class String
{
public:

    // Crea una instancia con todas las letras del parámetro 
    // convertidas a minúsculas.
    static String a_minusculas(const String& instancia_de_string);

    // Convierte todas las letras de ESTA instancia a minúsculas
    void a_minusculas(void)
    {
        *this = a_minusculas(*this);
    }
};

Enlace interno

Finalmente es posible declarar estáticos funciones y variables globales, es decir, fuera de una función. Una variable a nivel global existe durante toda la ejecución del programa sea estática o no. Y cualquier función tiene acceso a una variable global. Por eso static tiene otro significado fuera de un bloque {}: especifica que el identificador tiene enlace interno.

Enlace interno quiere decir, que el linker no puede usar este nombre para enlazarlo con otro módulo. Es un nombre privado en el fichero de objeto (.o o .obj). El contrario es un enlace externo: El fichero objeto expone este nombre y su tipo al linker que puede usar esta información para enlazarlo con una referencia en otro módulo.

El modificador static en una función o variable suprime el enlace externo. Se puede forzar un enlace externo con el modificador extern. Por defecto, funciones tienen un enlace externo y variables interno. Por eso no es necesario declarar una función extern y una variable static, pero mejora la semántica del código.

Conclusión

Hemos visto que la palabra clave static se usa para declarar variables de un alcance limitado pero con una vida durante toda la ejecución del programa. Métodos estáticos se limitan a acceder a variables miembros estáticos de la clase. Finalmente se usa el modificador static para forzar un enlace interno.

Como última nota cabe mencionar, que el uso de variables estáticas puede complicar la creación de procesos multi-hilo, ya que estos datos son comunes a todos los hilos.

Referencias

Anuncios