Pregunta

Si una variable se declara como static en el ámbito de una función, solo se inicializa una vez y conserva su valor entre las llamadas de función. ¿Qué es exactamente su vida útil? ¿Cuándo se llama a su constructor y destructor?

void foo() 
{ 
    static string plonk = "When will I die?";
}
¿Fue útil?

Solución

La vida útil de las variables static de la función comienza la primera vez que [0] el flujo del programa encuentra la declaración y finaliza al finalizar el programa. Esto significa que el tiempo de ejecución debe realizar algún mantenimiento de libros para destruirlo solo si se construyó realmente.

Además, dado que la norma dice que los destructores de objetos estáticos deben ejecutarse en el orden inverso a la finalización de su construcción [1] , y el orden de construcción puede depender de la ejecución del programa específico , el orden de la construcción debe tenerse en cuenta.

Ejemplo

struct emitter {
    string str;
    emitter(const string& s) : str(s) { cout << "Created " << str << endl; }
    ~emitter() { cout << "Destroyed " << str << endl; }
};

void foo(bool skip_first) 
{
    if (!skip_first)
        static emitter a("in if");
    static emitter b("in foo");
}

int main(int argc, char*[])
{
    foo(argc != 2);
    if (argc == 3)
        foo(false);
}

Output:

  

C: > sample.exe
  Creado en foo
  Destruido en foo

     

C: > sample.exe 1
  Creado en if
  Creado en foo
  Destruido en foo
  Destruido en si

     

C: > sample.exe 1 2
  Creado en foo
  Creado en if
  Destruido en si
  Destruido en foo

[0] Dado que C++98 [2] no tiene ninguna referencia a múltiples subprocesos sobre cómo se comportará esto en una el entorno de subprocesos no está especificado y puede ser problemático como Roddy menciona.

[1] C ++ 98 sección 3.6.3.1 [basic.start.term]

[2] En C ++ 11, las estadísticas están inicializadas de forma segura para subprocesos, esto también se conoce como Magic Statics .

Otros consejos

Motti tiene razón sobre el pedido, pero hay otras cosas que considerar:

Los compiladores suelen utilizar una variable de marca oculta para indicar si las estadísticas locales ya se han inicializado, y esta bandera se comprueba en cada entrada de la función. Obviamente, este es un pequeño golpe de rendimiento, pero lo que es más preocupante es que no se garantiza que este indicador sea seguro para subprocesos.

Si tiene una estática local como la anterior y se llama a foo desde varios subprocesos, es posible que haya condiciones de carrera que causen que plonk se inicialice incorrectamente o incluso varias veces. Además, en este caso, plonk puede ser destruido por un hilo diferente al que lo construyó.

A pesar de lo que dice la norma, desconfiaría del orden real de destrucción estática local, porque es posible que, sin saberlo, dependa de que un estático siga siendo válido después de que haya sido destruido, y esto es realmente difícil de rastrear. abajo.

Las explicaciones existentes no están realmente completas sin la regla real del Estándar, que se encuentra en 6.7:

  

La inicialización cero de todas las variables de ámbito de bloque con duración de almacenamiento estático o duración de almacenamiento de subprocesos se realiza antes de que tenga lugar cualquier otra inicialización. La inicialización constante de una entidad de alcance de bloque con duración de almacenamiento estático, si corresponde, se realiza antes de que se ingrese su bloque por primera vez. Se permite que una implementación realice una inicialización temprana de otras variables de ámbito de bloque con una duración de almacenamiento de subprocesos o estática en las mismas condiciones en que se permite que una implementación inicialice de forma estática una variable con una duración de almacenamiento de subprocesos o estática en el ámbito de espacio de nombres. De lo contrario, dicha variable se inicializa la primera vez que el control pasa por su declaración; dicha variable se considera inicializada una vez completada su inicialización. Si la inicialización sale lanzando una excepción, la inicialización   no está completo, por lo que se volverá a intentar la próxima vez que el control ingrese la declaración. Si el control ingresa la declaración concurrentemente mientras se inicializa la variable, la ejecución concurrente esperará la finalización de la inicialización. Si el control vuelve a ingresar la declaración de forma recursiva mientras se inicializa la variable, el comportamiento es indefinido.

FWIW, Codegear C ++ Builder no se destruye en el orden esperado según el estándar.

C:\> sample.exe 1 2
Created in foo
Created in if
Destroyed in foo
Destroyed in if

... ¡que es otra razón para no confiar en la orden de destrucción!

Las variables estáticas entran en juego una vez que comienza la ejecución del programa y permanecen disponibles hasta que finaliza la ejecución del programa.

Las variables estáticas se crean en el segmento de datos de la memoria .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top