¿Cuál es el tiempo de vida de una variable estática en una función de C ++?

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

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

PD: ¿Para aquellos que quieren saber por qué hice la pregunta si ya sabía la respuesta?

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

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

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); } 

Salida:

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

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

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

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

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

[2] En C ++ 11, las estáticas se inicializan de forma segura, esto también se conoce como Magic Statics .

Motti tiene razón sobre el orden, pero hay algunas otras cosas a considerar:

Los comstackdores suelen utilizar una variable de indicador oculto para indicar si las estadísticas estáticas locales ya se han inicializado, y este indicador se verifica en cada entrada de la función. Obviamente, este es un pequeño golpe de rendimiento, pero lo que más preocupa es que no se garantiza que esta bandera sea segura para subprocesos.

Si tiene una estática local como la anterior, y se llama a ‘foo’ desde varios subprocesos, puede tener 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 el estándar, sería muy cauteloso con respecto al orden real de la destrucción estática local, porque es posible que inconscientemente dependas de un ser estático que siga siendo válido después de haber sido destruido, y esto es realmente difícil de rastrear.

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

La inicialización a cero de todas las variables de ámbito de bloque con duración de almacenamiento estático o duración de almacenamiento de subprocesos se lleva a cabo antes de que tenga lugar cualquier otra inicialización. La inicialización constante de una entidad de ámbito de bloque con una duración de almacenamiento estática, si corresponde, se realiza antes de que se ingrese por primera vez su bloque. Se permite que una implementación realice una inicialización temprana de otras variables de scope de bloque con duración de almacenamiento estático o de subprocesos en las mismas condiciones en que se permite que una implementación inicialice de forma estática una variable con duración estática o de almacenamiento de subprocesos en el ámbito de espacio de nombres. De lo contrario, dicha variable se inicializa la primera vez que el control pasa por su statement; dicha variable se considera inicializada una vez completada su inicialización. Si la inicialización finaliza arrojando una excepción, la inicialización no está completa, por lo que se volverá a intentar la próxima vez que el control ingrese la statement. Si el control ingresa la statement simultáneamente mientras se está inicializando la variable, la ejecución concurrente deberá esperar hasta que se complete la inicialización. Si el control vuelve a ingresar a la statement recursivamente mientras se está inicializando la variable, el comportamiento no está definido.

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!