Ordine di inizializzazione statico C ++

Quando uso variabili statiche in C ++, spesso finisco per voler inizializzare una variabile passandone un’altra al suo costruttore. In altre parole, voglio creare istanze statiche che dipendono l’una dall’altra.

All’interno di un singolo file .cpp o .h questo non è un problema: le istanze verranno create nell’ordine in cui sono dichiarate. Tuttavia, quando si desidera inizializzare un’istanza statica con un’istanza in un’altra unità di compilazione, l’ordine sembra imansible da specificare. Il risultato è che, a seconda del tempo, può succedere che sia costruita l’istanza che dipende da un’altra, e solo successivamente viene costruita l’altra istanza. Il risultato è che la prima istanza è inizializzata in modo errato.

Qualcuno sa come garantire che gli oggetti statici vengano creati nell’ordine corretto? Ho cercato a lungo una soluzione, provandone tutte (compresa la soluzione Counter Schwarz), ma comincio a dubitare che ce ne sia una che funzioni davvero.

Una possibilità è il trucco con il membro della funzione statica:

Type& globalObject() { static Type theOneAndOnlyInstance; return theOneAndOnlyInstance; } 

In effetti, questo funziona. Purtroppo, devi scrivere globalObject (). MemberFunction (), invece di globalObject.MemberFunction (), risultante in un codice client un po ‘confuso e poco elegante.

Aggiornamento: grazie per le tue reazioni. Purtroppo, sembra proprio che abbia risposto alla mia stessa domanda. Credo che dovrò imparare a conviverci …

Hai risposto alla tua stessa domanda. L’ordine di inizializzazione statico non è definito, e il modo più elegante attorno ad esso (mentre si sta ancora iniziando l’inizializzazione statica, ovvero non lo si rifà completamente) è di avvolgere l’inizializzazione in una funzione.

Leggi le domande frequenti su C ++ a partire da https://isocpp.org/wiki/faq/ctors#static-init-order

Forse dovresti riconsiderare se hai bisogno di così tante variabili statiche globali. Anche se a volte possono essere utili, spesso è molto più semplice rifattorizzarli su un ambito locale più piccolo, specialmente se si scopre che alcune variabili statiche dipendono da altre.

Ma hai ragione, non c’è modo di garantire un particolare ordine di inizializzazione, e quindi se il tuo cuore è impostato su di esso, mantenere l’inizializzazione in una funzione, come hai detto tu, è probabilmente il modo più semplice.

In effetti, questo funziona. Purtroppo, devi scrivere globalObject (). MemberFunction (), invece di globalObject.MemberFunction (), risultante in un codice client un po ‘confuso e poco elegante.

Ma la cosa più importante è che funzioni, e che sia a prova di fallimento, cioè. non è facile aggirare l’uso corretto.

La correttezza del programma dovrebbe essere la tua prima priorità. Inoltre, IMHO, il () sopra è puramente stilistico – vale a dire. completamente irrilevante.

A seconda della piattaforma, fai attenzione a troppe inizializzazioni dinamiche. C’è una quantità relativamente piccola di ripulitura che può aver luogo per gli inizializzatori dinamici (vedi qui ). È ansible risolvere questo problema utilizzando un contenitore di oggetti globale che contiene membri diversi oggetti globali. Quindi hai:

 Globals & getGlobals () { static Globals cache; return cache; } 

C’è solo una chiamata a ~ Globals () per ripulire tutti gli oggetti globali nel tuo programma. Per accedere a un globale hai ancora qualcosa di simile:

 getGlobals().configuration.memberFunction (); 

Se vuoi davvero che tu possa racchiuderlo in una macro per risparmiare un po ‘di digitazione usando una macro:

 #define GLOBAL(X) getGlobals().#X GLOBAL(object).memberFunction (); 

Anche se, questo è solo zucchero sintattico sulla tua soluzione iniziale.

La maggior parte dei compilatori (linker) in realtà supportano un modo (non portabile) di specificare l’ordine. Ad esempio, con Visual Studio è ansible utilizzare il pragma init_seg per organizzare l’inizializzazione in diversi gruppi diversi. AFAIK non c’è modo di garantire l’ordine ENTRO ogni gruppo. Dal momento che questo non è portabile, potresti voler considerare se è ansible correggere il tuo progetto per non averlo richiesto, ma l’opzione è disponibile.

a dispetto dell’età di questo thread, vorrei proporre la soluzione che ho trovato. Come molti hanno sottolineato prima di me, C ++ non fornisce alcun meccanismo per l’ordinamento di inizializzazione statico. Quello che propongo è di incapsulare ogni membro statico all’interno di un metodo statico della class che a sua volta inizializza il membro e fornisce un accesso in modo orientato agli oggetti. Lasciatemi fare un esempio, supponendo di voler definire la class chiamata “Math” che, tra gli altri membri, contiene “PI”:

 class Math { public: static const float Pi() { static const float s_PI = 3.14f; return s_PI; } } 

s_PI verrà inizializzato la prima volta che viene richiamato il metodo Pi () (in GCC). Attenzione: gli oggetti locali con memoria statica hanno un ciclo di vita dipendente dall’implementazione, per ulteriori dettagli controllare 6.7.4 in 2 .

Parola chiave statica , standard C ++

Avvolgere la statica in un metodo risolverà il problema dell’ordine, ma non è thread-safe come altri hanno sottolineato, ma puoi farlo anche per renderlo thread se questo è un problema.

 // File scope static pointer is thread safe and is initialized first. static Type * theOneAndOnlyInstance = 0; Type& globalObject() { if(theOneAndOnlyInstance == 0) { // Put mutex lock here for thread safety theOneAndOnlyInstance = new Type(); } return *theOneAndOnlyInstance; }