2008-10-29 21 views
289

Si una variable se declara como static en el alcance 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?¿Cuál es el tiempo de vida de una variable estática en una función de C++?

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

P. S. Para aquellos que quieren saber why I asked the question if I already knew the answer?

+17

Normalmente no voto las preguntas donde el asker respondió de inmediato a sí mismo, pero este es interesante. Gracias por sacar el tema. –

+0

@Motti Link está muerto. – Zaimatsu

+0

@Zaimatsu el enlace todavía funciona para mí. – Motti

Respuesta

177

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

Adicionalmente ya que la norma dice que los destructores de objetos estáticos se deben ejecutar en el orden inverso al de la finalización de su construcción [1] y el orden de la construcción puede depender de la ejecución del programa específico, el fin de la construcción debe ser tomado 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); 
} 

de salida:

C:> sample.exe
creado en foo
destruido en foo

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

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

[0] Desde C++ 98[2] no tiene referencia a múltiples hilos cómo se comportará en un entorno de subprocesos múltiples no 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 estática se inicializan de forma segura hilo, esto también se conoce como Magic Statics.

+2

Para tipos simples sin efectos secundarios c'tor/d'tor, es una optimización directa para inicializarlos de la misma manera que los tipos simples globales. Esto evita los problemas de ramificación, bandera y orden de destrucción. Eso no quiere decir que su vida sea diferente. –

+0

¿Qué hay de C++ 11? – allyourcode

+0

Si varios subprocesos pueden invocar la función, ¿significa que debe asegurarse de que las declaraciones estáticas deben estar protegidas por un mutex en C++ 98? – allyourcode

114

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

Los compiladores suelen utilizar una variable indicador oculto para indicar si la estática locales ya han sido inicializado, y esta bandera se comprueba en cada entrada a 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 de arriba, y se llama a 'foo' desde varios hilos, 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 la norma, sería muy cauteloso con respecto al orden real de la destrucción estática local, porque es posible que inconscientemente dependa de un estado estático aún válido después de haber sido destruido, y esto es realmente difícil rastrear.

+56

C++ 0x requiere que la inicialización estática sea segura para subprocesos. Así que ten cuidado, pero las cosas solo mejorarán. –

+0

Se pueden evitar los problemas de orden de destrucción con una pequeña política. los objetos estáticos/globales (singletons, etc.) no tendrán acceso a otros objetos estáticos en sus cuerpos de métodos. Solo se accederá en constructores donde se pueda almacenar una referencia/puntero para acceder posteriormente a los métodos. Esto no es perfecto, pero debería arreglar 99 de los casos y los casos que no capta son obviamente sospechosos y deberían ser atrapados en una revisión del código. Esto todavía no es una solución perfecta, ya que la política no se puede aplicar en el idioma –

+0

Soy un poco novato, pero ¿por qué esta política no se puede aplicar en el idioma? – cjcurrie

8

FWIW, Codegear C++ Builder no se destruye en el orden esperado de acuerdo con la norma.

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!

+49

No es un buen argumento. Yo diría que esto es más un argumento para no usar este compilador. –

+22

Hmm. Si está interesado en producir código portátil del mundo real, en lugar de un código teóricamente portátil, creo que es útil saber qué áreas del lenguaje pueden causar problemas. Me sorprendería si C++ Builder fuera único al no manejar esto. – Roddy

+13

Estoy de acuerdo, excepto que lo expresaría como "qué compiladores causan problemas y en qué áreas del lenguaje lo hacen" ;-P –

9

Las explicaciones existentes no son realmente completa sin la regla actual de la norma, que se encuentra en 6.7:

se lleva a cabo el cero de inicialización de todas las variables del bloque de alcance con una duración de almacenamiento estático o duración de almacenamiento de hilo antes de que se lleve a cabo 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 alcance 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 declaración; 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 declaración. Si el control ingresa la declaración 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 declaración recursivamente mientras se está inicializando la variable, el comportamiento no está definido.

Cuestiones relacionadas