Qual è la durata di una variabile statica in una funzione C ++?
Domanda
Se una variabile viene dichiarata come static
nell'ambito di una funzione, viene inizializzata una sola volta e mantiene il suo valore tra le chiamate di funzione. Qual è esattamente la sua vita? Quando vengono chiamati il ??suo costruttore e distruttore?
void foo()
{
static string plonk = "When will I die?";
}
Soluzione
La durata della variabile statica
inizia la prima volta [0] il flusso del programma incontra la dichiarazione e termina al termine del programma. Ciò significa che il tempo di esecuzione deve eseguire alcuni libri per poterlo distruggere solo se è stato effettivamente costruito.
Inoltre, poiché lo standard afferma che i distruttori di oggetti statici devono funzionare nell'ordine inverso rispetto al completamento della loro costruzione [1] , e l'ordine di costruzione può dipendere dalla specifica esecuzione del programma , l'ordine di costruzione deve essere preso in considerazione.
Esempio
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
Creato in foo
Distrutto a piè di paginaC: > sample.exe 1
Creato in se
Creato in foo
Distrutto nel foo
Distrutto in seC: > sample.exe 1 2
Creato in foo
Creato in se
Distrutto in se
Distrutto a piè di pagina
[0]
Poiché C++98 [2] non ha alcun riferimento a più thread su come ciò si comporterà in un multi- l'ambiente thread non è specificato e può essere problematico come Roddy menziona.
[1]
C ++ 98 sezione 3.6.3.1
[basic.start.term]
[2]
In C ++ 11 le statistiche sono inizializzate in modo thread-safe, questo è anche noto come Magic Statics .
Altri suggerimenti
Motti ha ragione sull'ordine, ma ci sono alcune altre cose da considerare:
I compilatori utilizzano in genere una variabile di flag nascosta per indicare se le statistiche locali sono già state inizializzate e questo flag viene controllato su ogni voce della funzione. Ovviamente si tratta di un piccolo successo in termini di prestazioni, ma ciò che più preoccupa è che questo flag non è garantito per essere thread-safe.
Se hai una statica locale come sopra, e foo
viene chiamato da più thread, potresti avere condizioni di competizione che causano l'inizializzazione errata di plonk
o anche più volte. Inoltre, in questo caso plonk
potrebbe essere distrutto da un thread diverso da quello che lo ha costruito.
Nonostante ciò che dice lo standard, sarei molto cauto sull'ordine reale di distruzione statica locale, perché è possibile che tu possa inconsapevolmente fare affidamento su una staticità ancora valida dopo che è stata distrutta, e questo è davvero difficile da rintracciare giù.
Le spiegazioni esistenti non sono realmente complete senza la regola effettiva dello Standard, trovata in 6.7:
L'inizializzazione zero di tutte le variabili con ambito di blocco con durata dell'archiviazione statica o durata dell'archiviazione del thread viene eseguita prima di qualsiasi altra inizializzazione. L'inizializzazione costante di un'entità con ambito di blocco con durata di memorizzazione statica, se applicabile, viene eseguita prima che il blocco venga immesso per la prima volta. Un'implementazione può eseguire l'inizializzazione anticipata di altre variabili con ambito di blocco con durata dell'archiviazione statica o thread nelle stesse condizioni in cui è consentita un'implementazione per inizializzare staticamente una variabile con durata dell'archiviazione statica o thread nell'ambito dello spazio dei nomi. Altrimenti una variabile del genere viene inizializzata la prima volta che il controllo passa attraverso la sua dichiarazione; tale variabile è considerata inizializzata al completamento della sua inizializzazione. Se l'inizializzazione termina generando un'eccezione, l'inizializzazione non è completo, quindi verrà riprovato la prossima volta che il controllo entra nella dichiarazione. Se il controllo inserisce la dichiarazione contemporaneamente mentre la variabile viene inizializzata, l'esecuzione simultanea deve attendere il completamento dell'inizializzazione. Se il controllo reinserisce la dichiarazione in modo ricorsivo durante l'inizializzazione della variabile, il comportamento non è definito.
FWIW, Codegear C ++ Builder non si distrugge nell'ordine previsto secondo lo standard.
C:\> sample.exe 1 2
Created in foo
Created in if
Destroyed in foo
Destroyed in if
... che è un altro motivo per non fare affidamento sull'ordine di distruzione!
Le variabili statiche entrano in gioco una volta che inizia l'esecuzione del programma e rimangono disponibili fino al termine dell'esecuzione del programma.
Le variabili statiche vengono create nel Segmento di dati della memoria .