c++ - mathematics - wikipedia factory pattern




Modello di progettazione Singleton C++ (12)

È possibile evitare l'allocazione di memoria. Ci sono molte varianti, tutte con problemi in caso di ambiente multithreading.

Preferisco questo tipo di implementazione (in realtà, non è detto correttamente che preferisco, perché evito il più possibile i singleton):

class Singleton
{
private:
   Singleton();

public:
   static Singleton& instance()
   {
      static Singleton INSTANCE;
      return INSTANCE;
   }
};

Non ha allocazione dinamica della memoria.

Recentemente mi sono imbattuto in una realizzazione / implementazione del modello di progettazione Singleton per C ++. È sembrato questo (l'ho adottato dall'esempio della vita reale):

// a lot of methods are omitted here
class Singleton
{
   public:
       static Singleton* getInstance( );
       ~Singleton( );
   private:
       Singleton( );
       static Singleton* instance;
};

Da questa dichiarazione posso dedurre che il campo istanza viene avviato sull'heap. Ciò significa che c'è un'allocazione di memoria. Ciò che è completamente poco chiaro per me è quando esattamente la memoria sarà dislocata? O c'è un bug e una perdita di memoria? Sembra che ci sia un problema nell'implementazione.

La mia domanda principale è: come posso implementarla nel modo giusto?


Che ne dici di usare il posizionamento in questo modo:

class singleton
{
    static singleton *s;
    static unsigned char *buffer[sizeof(singleton)/4 *4] //4 byte align
    static singleton* getinstance()
    {
        if (s == null)
        {
            s = new(buffer) singleton;
        }
        return s;
    }
};

Essendo un Singleton, di solito non vuoi che venga distrutto.

Verrà abbattuto e deallocato al termine del programma, che è il comportamento normale desiderato per un singleton. Se vuoi essere in grado di pulirlo in modo esplicito, è abbastanza facile aggiungere alla classe un metodo statico che ti permetta di ripristinarlo in uno stato pulito, e farlo riallocare la prossima volta che viene usato, ma questo è al di fuori dell'ambito di un singleton "classico".


In effetti è probabilmente assegnato dall'heap, ma senza le fonti non c'è modo di saperlo.

L'implementazione tipica (presa da un codice che ho già in emacs) sarebbe:

Singleton * Singleton::getInstance() {
    if (!instance) {
        instance = new Singleton();
    };
    return instance;
};

... e fare affidamento sul programma andando fuori dal campo di applicazione per ripulire in seguito.

Se lavori su una piattaforma in cui la pulizia deve essere eseguita manualmente, probabilmente aggiungerei una routine di pulizia manuale.

Un altro problema nel farlo in questo modo è che non è thread-safe. In un ambiente con multithreading, due thread potrebbero attraversare il "se" prima che entrambi abbiano la possibilità di allocare la nuova istanza (così entrambi lo farebbero). Questo non è ancora un grosso problema se si sta facendo affidamento sulla terminazione del programma per ripulire comunque.


La soluzione nella risposta accettata ha un notevole svantaggio: il distruttore per il singleton viene chiamato dopo che il controllo lascia la funzione main() . Potrebbero esserci dei problemi in realtà, quando alcuni oggetti dipendenti sono allocati all'interno del main .

Ho incontrato questo problema, quando ho provato a introdurre un Singleton nell'applicazione Qt. Ho deciso, che tutte le mie finestre di dialogo di installazione devono essere Singletons e hanno adottato lo schema sopra. Sfortunatamente, la QApplication principale di Qt è stata allocata nello stack nella funzione main e Qt proibisce la creazione / distruzione di finestre di dialogo quando nessun oggetto dell'applicazione è disponibile.

Questo è il motivo per cui preferisco i singleton allocati su heap. Fornisco metodi espliciti init() e term() per tutti i singleton e li chiamiamo all'interno main . Così ho il pieno controllo sull'ordine di creazione / distruzione dei singleton, e inoltre garantisco che i singleton verranno creati, indipendentemente dal fatto che qualcuno abbia chiamato getInstance() o meno.


Nel 2008 ho fornito un'implementazione C ++ 98 del modello di progettazione Singleton che è a valutazione pigro, ha garantito la distruzione, non è tecnicamente thread-safe:
Qualcuno può fornirmi un campione di Singleton in c ++?

Ecco un'implementazione aggiornata di C ++ 11 del modello di progettazione Singleton che è valutato in modo pigro, correttamente distrutto e thread-safe .

class S
{
    public:
        static S& getInstance()
        {
            static S    instance; // Guaranteed to be destroyed.
                                  // Instantiated on first use.
            return instance;
        }
    private:
        S() {}                    // Constructor? (the {} brackets) are needed here.

        // C++ 03
        // ========
        // Don't forget to declare these two. You want to make sure they
        // are unacceptable otherwise you may accidentally get copies of
        // your singleton appearing.
        S(S const&);              // Don't Implement
        void operator=(S const&); // Don't implement

        // C++ 11
        // =======
        // We can use the better technique of deleting the methods
        // we don't want.
    public:
        S(S const&)               = delete;
        void operator=(S const&)  = delete;

        // Note: Scott Meyers mentions in his Effective Modern
        //       C++ book, that deleted functions should generally
        //       be public as it results in better error messages
        //       due to the compilers behavior to check accessibility
        //       before deleted status
};

Vedi questo articolo su quando usare un singleton: (non spesso)
Singleton: come dovrebbe essere usato

Vedi questo articolo sull'ordine di inizializzazione e su come far fronte:
Ordine di inizializzazione delle variabili statiche
Ricerca di problemi di ordine di inizializzazione statici C ++

Vedi questo articolo che descrive le vite:
Qual è la durata di una variabile statica in una funzione C ++?

Vedi questo articolo che discute alcune implicazioni di threading ai singleton:
L'istanza Singleton dichiarata come variabile statica del metodo GetInstance, è thread-safe?

Vedi questo articolo che spiega perché il double check locking non funzionerà su C ++:
Quali sono tutti i comuni comportamenti indefiniti di cui un programmatore C ++ dovrebbe essere a conoscenza?
Dr Dobbs: C ++ e The Perils of Double-Checked Locking: Part I


Oltre all'altra discussione qui, può valere la pena notare che è possibile avere una globalizzazione, senza limitare l'utilizzo a un'istanza. Ad esempio, considera il caso di riferimento che conta qualcosa ...

struct Store{
   std::array<Something, 1024> data;
   size_t get(size_t idx){ /* ... */ }
   void incr_ref(size_t idx){ /* ... */}
   void decr_ref(size_t idx){ /* ... */}
};

template<Store* store_p>
struct ItemRef{
   size_t idx;
   auto get(){ return store_p->get(idx); };
   ItemRef() { store_p->incr_ref(idx); };
   ~ItemRef() { store_p->decr_ref(idx); };
};

Store store1_g;
Store store2_g; // we don't restrict the number of global Store instances

Ora da qualche parte all'interno di una funzione (come main ) puoi fare:

auto ref1_a = ItemRef<&store1_g>(101);
auto ref2_a = ItemRef<&store2_g>(201); 

Gli arbitri non hanno bisogno di memorizzare un puntatore sul rispettivo Store perché tali informazioni sono fornite in fase di compilazione. Inoltre, non devi preoccuparti della durata del Store perché il compilatore richiede che sia globale. Se in effetti esiste solo un'istanza di Store non c'è un sovraccarico in questo approccio; con più di un'istanza spetta al compilatore essere intelligente sulla generazione del codice. Se necessario, la classe ItemRef può anche diventare friend di Store (puoi avere amici con modelli!).

Se Store stesso è una classe basata su modelli, le cose diventano più complicate, ma è comunque possibile utilizzare questo metodo, forse implementando una classe helper con la seguente firma:

template <typename Store_t, Store_t* store_p>
struct StoreWrapper{ /* stuff to access store_p, e.g. methods returning 
                       instances of ItemRef<Store_t, store_p>. */ };

L'utente può ora creare un tipo StoreWrapper (e un'istanza globale) per ogni istanza di Store globale e accedere sempre ai negozi tramite l'istanza del wrapper (dimenticando quindi i dettagli sanguinosi dei parametri del modello necessari per l'utilizzo di Store ).


Penso che dovresti scrivere una funzione statica in cui il tuo oggetto statico viene cancellato. Dovresti chiamare questa funzione quando stai per chiudere la tua applicazione. Ciò garantirà che non ci siano perdite di memoria.


Se si desidera allocare l'oggetto in heap, perché non utilizzare un puntatore univoco. Anche la memoria verrà deallocata poiché utilizziamo un puntatore univoco.

class S
{
    public:
        static S& getInstance()
        {
            if( m_s.get() == 0 )
            {
              m_s.reset( new S() );
            }
            return *m_s;
        }

    private:
        static std::unique_ptr<S> m_s;

        S();
        S(S const&);            // Don't Implement
        void operator=(S const&); // Don't implement
};

std::unique_ptr<S> S::m_s(0);

Semplice classe singleton, Questo deve essere il tuo file di classe di intestazione

#ifndef SC_SINGLETON_CLASS_H
#define SC_SINGLETON_CLASS_H

class SingletonClass
{
    public:
        static SingletonClass* Instance()
        {
           static SingletonClass* instance = new SingletonClass();
           return instance;
        }

        void Relocate(int X, int Y, int Z);

    private:
        SingletonClass();
        ~SingletonClass();
};

#define sSingletonClass SingletonClass::Instance()

#endif

Accedi al tuo singleton in questo modo:

sSingletonClass->Relocate(1, 2, 5);

Un'altra alternativa non allocante: crea un singleton, ad esempio della classe C , in base alle tue esigenze:

singleton<C>()

utilizzando

template <class X>
X& singleton()
{
    static X x;
    return x;
}

Né questa né la risposta di Cătălin sono automaticamente thread-safe nell'attuale C ++, ma saranno in C ++ 0x.


La risposta di @Loki Astari è eccellente.

Tuttavia ci sono volte con più oggetti statici in cui è necessario essere in grado di garantire che il singleton non verrà distrutto finché tutti gli oggetti statici che utilizzano il singleton non ne avranno più bisogno.

In questo caso, std::shared_ptr può essere usato per mantenere vivo il singleton per tutti gli utenti anche quando i distruttori statici vengono chiamati alla fine del programma:

class Singleton
{
public:
    Singleton(Singleton const&) = delete;
    Singleton& operator=(Singleton const&) = delete;

    static std::shared_ptr<Singleton> instance()
    {
        static std::shared_ptr<Singleton> s{new Singleton};
        return s;
    }

private:
    Singleton() {}
};




singleton