Domanda

Quando uso variabili statiche in C ++, finisco spesso per voler inizializzare una variabile passando 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 vengono dichiarate. Tuttavia, quando si desidera inizializzare un'istanza statica con un'istanza in un'altra unità di compilazione, l'ordine sembra impossibile da specificare. Il risultato è che, a seconda del tempo, può accadere che venga 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 assicurarsi che gli oggetti statici vengano creati nell'ordine corretto? Ho cercato a lungo una soluzione, provandole tutte (compresa la soluzione Schwarz Counter), ma inizio a dubitare che ce ne sia una che funzioni davvero.

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

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

In effetti, questo funziona. Purtroppo, devi scrivere globalObject (). MemberFunction (), invece di globalObject.MemberFunction (), risultando in un codice client un po 'confuso e inelegante.

Aggiornamento: Grazie per le tue reazioni. Purtroppo, sembra davvero che io abbia risposto alla mia domanda. Immagino che dovrò imparare a conviverci ...

È stato utile?

Soluzione

Hai risposto alla tua domanda. L'ordine di inizializzazione statica non è definito e il modo più elegante per aggirarlo (pur continuando a eseguire l'inizializzazione statica, cioè non rimuoverlo 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-ordine

Altri suggerimenti

Forse dovresti riconsiderare se hai bisogno di così tante variabili statiche globali. Sebbene a volte possano essere utili, spesso è molto più semplice convertirli in 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, quindi se il tuo cuore è impostato su di esso, mantenere l'inizializzazione in una funzione, come hai menzionato, è probabilmente il modo più semplice.

  

In effetti, questo funziona. Purtroppo, devi scrivere globalObject (). MemberFunction (), invece di globalObject.MemberFunction (), risultando in un codice client un po 'confuso e inelegante.

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

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

A seconda della piattaforma, fare attenzione all'inizializzazione troppo dinamica. C'è una quantità relativamente piccola di ripulitura che può avvenire per inizializzatori dinamici (vedi qui ). È possibile risolvere questo problema utilizzando un contenitore di oggetti globale che contiene membri diversi oggetti globali. Pertanto 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 come:

getGlobals().configuration.memberFunction ();

Se davvero lo volessi, potresti inserirlo in una macro per salvare un po 'di battitura 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à supporta un modo (non portatile) di specificare l'ordine. Ad esempio, con Visual Studio è possibile utilizzare init_seg pragma per organizzare l'inizializzazione in diversi gruppi diversi. AFAIK non è possibile garantire l'ordine all'interno di ciascun gruppo. Dal momento che questo non è portatile, potresti prendere in considerazione la possibilità di correggere il tuo progetto per non richiederlo, ma l'opzione è disponibile.

nonostante l'età di questa discussione, vorrei proporre la soluzione che ho trovato. Come molti hanno sottolineato prima di me, C ++ non fornisce alcun meccanismo per l'ordinamento di inizializzazione statica. Ciò che propongo è di incapsulare ciascun membro statico all'interno di un metodo statico della classe che a sua volta inizializza il membro e fornisce un accesso orientato agli oggetti. Lascia che ti faccia un esempio, supponendo di voler definire la classe denominata " 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 al primo richiamo del 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 ++

L'avvolgimento dell'elettricità statica in un metodo risolverà il problema dell'ordine, ma non è sicuro per il thread 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;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top