Singolo C ++ rispetto all’object statico globale

Un mio amico oggi mi ha chiesto perché dovrebbe preferire l’uso del singleton rispetto all’object statico globale? Il modo in cui ho iniziato a spiegare era che il singleton può avere un object statico o statico globale non … ma non ne ero sicuro … perché questo in C ++ .. (venivo da C #)

Quali sono i vantaggi l’uno rispetto all’altro? (in C ++)

In realtà, in C ++ il modo preferito è l’object statico locale.

Printer & thePrinter() { static Printer printer; return printer; } 

Questo è tecnicamente un singleton, tuttavia, questa funzione può anche essere un metodo statico di una class. Quindi garantisce di essere costruito prima dell’uso a differenza degli oggetti statici globali, che possono essere creati in qualsiasi ordine, rendendo ansible fallire in modo incoerente quando un object globale utilizza un altro, piuttosto uno scenario comune.

Ciò che lo rende migliore del modo comune di fare singleton con la creazione di una nuova istanza chiamando new è che il distruttore dell’object verrà chiamato alla fine di un programma. Non succederà con singleton assegnato in modo dinamico.

Un altro lato positivo è che non è ansible accedere a singleton prima che venga creato, anche da altri metodi statici o da sottoclassi. Ti fa risparmiare tempo per il debug.

In C ++, l’ordine di istanziazione di oggetti statici in diverse unità di compilazione non è definito. Quindi è ansible che un globale faccia riferimento a un altro che non è costruito, facendo esplodere il programma. Il modello singleton rimuove questo problema legando la costruzione a una funzione membro statica o funzione libera.

C’è un riassunto decente qui .

Un mio amico oggi mi ha chiesto perché dovrebbe preferire l’uso del singleton rispetto all’object statico globale? Il modo in cui ho iniziato a spiegare era che il singleton può avere un object statico o statico globale non … ma non ne ero sicuro … perché questo in C ++ .. (venivo da C #)

Un object statico globale può avere lo stato anche in C #:

 class myclass { // can have state // ... public static myclass m = new myclass(); // globally accessible static instance, which can have state } 

Quali sono i vantaggi l’uno rispetto all’altro? (in C ++)

Un singleton paralizza il tuo codice, un’istanza statica globale no. Ci sono innumerevoli domande su SO sui problemi con i singleton. Ecco uno e un altro o un altro .

In breve, un singleton ti dà due cose:

  • un object globalmente accessibile, e
  • una garanzia che è ansible creare solo un’istanza.

Se vogliamo solo il primo punto, dovremmo creare un object accessibile a livello globale. E perché vorremmo mai il secondo? Non sappiamo in anticipo come il nostro codice possa essere utilizzato in futuro, quindi perché inchiodarlo e rimuovere quella che potrebbe essere una funzionalità utile? Di solito sbagliamo quando prevediamo che “avrò bisogno di una sola istanza”. E c’è una grande differenza tra “Mi servirà solo un’istanza” (la risposta corretta è quindi quella di creare un’istanza) e “l’applicazione non può in alcun caso funzionare correttamente se viene creata più di una istanza. formattare l’hard disk dell’utente e pubblicare i dati sensibili su Internet “(la risposta qui è la seguente: molto probabilmente la tua app è rotta, ma se non lo è, allora sì, un singleton è ciò di cui hai bisogno)

Motivo 1:
I single sono facili da creare, quindi sono build pigri.
Anche se puoi farlo con le globali, il lavoro dello sviluppatore richiede molto lavoro. Quindi, per impostazione predefinita, i globals sono sempre inizializzati (a parte alcune regole speciali con namespace).

Quindi, se il tuo object è di grandi dimensioni e / o costoso da build, potresti non volerlo build a meno che tu non debba davvero usarlo.

Motivo 2:
Ordine di inizializzazione (e distruzione) problema.

 GlobalRes& getGlobalRes() { static GlobalRes instance; // Lazily initialized. return instance; } GlobalResTwo& getGlobalResTwo() { static GlobalResTwo instance; // Lazy again. return instance; } // Order of destruction problem. // The destructor of this object uses another global object so // the order of destruction is important. class GlobalResTwo { public: GlobalResTwo() { getGlobalRes(); // At this point globalRes is fully initialized. // Because it is fully initialized before this object it will be destroyed // after this object is destroyed (Guaranteed) } ~GlobalResTwo() { // It is safe to use globalRes because we know it will not be destroyed // before this object. getGlobalRes().doStuff(); } }; 

Un altro vantaggio di Singleton rispetto all’object statico globale è che poiché la funzione di costruzione è privata, esiste una direttiva applicata al compilatore molto chiara che dice “Può essercene una sola”.

In confronto, con l’object statico globale, non ci sarà nulla che fermi un codice di scrittura sviluppatore che crei un’istanza aggiuntiva di questo object.

Il vantaggio del vincolo extra è che hai una garanzia su come l’object sarà usato.

Utilizzando l’idioma Singleton (“costrutto al primo utilizzo”), è ansible evitare il fiasco dell’ordine di inizializzazione statico

In C ++, non c’è un’enorme differenza tra i due in termini di utilità effettiva. Un object globale può naturalmente mantenere il proprio stato (eventualmente con altre variabili globali, sebbene io non lo raccomandi). Se vuoi usare un global o un singleton (e ci sono molte ragioni per non farlo), la ragione principale per usare un singleton su un object globale è che con un singleton, puoi avere un polimorfismo dinamico avendo diverse classi che ereditano da una class base singleton.

OK, ci sono due ragioni per andare davvero con un singleton. Uno è l’ordine statico di cui parlano tutti.

L’altro è per impedire a qualcuno di fare qualcosa di simile quando si utilizza il codice:

 CoolThing blah; gs_coolGlobalStaticThing = blah; 

o, ancora peggio:

 gs_coolGlobalStaticThing = {}; 

L’aspetto dell’incapsulamento proteggerà la tua istanza da idioti e cretini maliziosi.