Padrão de design C ++ Singleton

Recentemente eu esbarrei em uma realização / implementação do padrão de design Singleton para C ++. Foi assim (eu adotei do exemplo da vida real):

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

A partir dessa declaração, posso deduzir que o campo de instância é iniciado no heap. Isso significa que há uma alocação de memory. O que não é claro para mim é quando exatamente a memory será desalocada? Ou há um vazamento de bug e memory? Parece que há um problema na implementação.

A minha principal questão é, como posso implementá-lo da maneira certa?

Veja este artigo para um projeto simples para um avaliado preguiçoso com singleton de destruição garantida:
Alguém pode me fornecer uma amostra de Singleton em c ++?

O clássico preguiçoso avaliou e destruiu corretamente o singleton.

 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 }; 

Veja este artigo sobre quando usar um singleton: (não frequentemente)
Singleton: Como deve ser usado

Veja este artigo sobre a ordem de boot e como lidar:
Ordem de boot de variables ​​estáticas
Encontrando problemas de ordem de boot estática do C ++

Veja este artigo descrevendo as vidas:
Qual é o tempo de vida de uma variável estática em uma function C ++?

Veja este artigo que discute algumas implicações de threading para singletons:
Instância de Singleton declarada como variável estática do método GetInstance

Veja este artigo que explica porque o bloqueio duplo verificado não funcionará no C ++:
Quais são os comportamentos indefinidos comuns que um programador C ++ deve conhecer?
Dr. Dobbs: C ++ e os perigos do bloqueio duplo: parte I

Sendo um Singleton, você geralmente não quer que ele seja destruído.

Ele será demolido e desalocado quando o programa terminar, que é o comportamento normal e desejado de um singleton. Se você quiser poder limpá-lo explicitamente, é bastante fácil adicionar um método estático à class que permita restaurá-lo a um estado limpo e fazer com que ele seja realocado na próxima vez em que for usado, mas isso está fora do escopo de um “clássico” singleton.

Você pode evitar a alocação de memory. Existem muitas variantes, todas com problemas no caso de ambientes multithreading.

Eu prefiro este tipo de implementação (na verdade, não é correto dizer que eu prefiro, porque eu evito singletons tanto quanto possível):

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

Não possui alocação de memory dinâmica.

Outra alternativa de não alocação: crie um singleton, digamos, da class C , conforme necessário:

 singleton() 

usando

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

Nem esta nem a resposta de Cătălin é automaticamente thread-safe no C ++ atual, mas estará em C ++ 0x.

A resposta de @Loki Astari é excelente.

No entanto, há momentos com múltiplos objects estáticos onde você precisa garantir que o singleton não será destruído até que todos os seus objects estáticos que usam o singleton não precisem mais dele.

Nesse caso, std::shared_ptr pode ser usado para manter o singleton vivo para todos os usuários, mesmo quando os destrutores estáticos estiverem sendo chamados no final do programa:

 class Singleton { public: Singleton(Singleton const&) = delete; Singleton& operator=(Singleton const&) = delete; static std::shared_ptr instance() { static std::shared_ptr s{new Singleton}; return s; } private: Singleton() {} }; 

Se você quiser alocar o object na pilha, por que não usar um ponteiro único. A memory também será desalocada, já que estamos usando um ponteiro único.

 class S { public: static S& getInstance() { if( m_s.get() == 0 ) { m_s.reset( new S() ); } return *m_s; } private: static std::unique_ptr m_s; S(); S(S const&); // Don't Implement void operator=(S const&); // Don't implement }; std::unique_ptr S::m_s(0); 

A solução na resposta aceita tem uma desvantagem significativa – o destruidor do singleton é chamado depois que o controle deixa a function “principal”. Pode haver problemas, na verdade, quando alguns objects dependentes são alocados dentro de “main”.

Eu encontrei este problema, ao tentar introduzir um Singleton no aplicativo Qt. Eu decidi que todos os meus diálogos de configuração devem ser Singletons, e adotei o padrão acima. Infelizmente, a class principal “QApplication” do Qt foi alocada na pilha na function “main”, e o Qt proíbe criar / destruir diálogos quando nenhum object do aplicativo estiver disponível.

É por isso que eu prefiro singletons alocados em heap. Eu forneço um explícito “init ()” e “term ()” methods para todos os singletons e chamá-los dentro de “main”. Assim, eu tenho um controle total sobre a ordem de criação / destruição de singletons, e também garanto que singletons serão criados, não importa se alguém chamou “getInstance ()” ou não.

Aqui está uma implementação fácil.

 #include  #include  using namespace std; class SingletonClass { public: static SingletonClass* getInstance() { return (!m_instanceSingleton) ? m_instanceSingleton = new SingletonClass : m_instanceSingleton; } private: // private constructor and destructor SingletonClass() { cout << "SingletonClass instance created!\n"; } ~SingletonClass() {} // private copy constructor and assignment operator SingletonClass(const SingletonClass&); SingletonClass& operator=(const SingletonClass&); static SingletonClass *m_instanceSingleton; }; SingletonClass* SingletonClass::m_instanceSingleton = nullptr; int main(int argc, const char * argv[]) { SingletonClass *singleton; singleton = singleton->getInstance(); cout << singleton << endl; // Another object gets the reference of the first object! SingletonClass *anotherSingleton; anotherSingleton = anotherSingleton->getInstance(); cout << anotherSingleton << endl; Sleep(5000); return 0; } 

Apenas um object criado e essa referência de object é retornada sempre após as palavras-chave.

 SingletonClass instance created! 00915CB8 00915CB8 

Aqui 00915CB8 é a localização da memory do object singleton, mesmo para a duração do programa, mas (normalmente!) Diferente cada vez que o programa é executado.

NB Este não é um thread safe one.Você tem que garantir a segurança do segmento.

Na verdade, é provavelmente alocado do heap, mas sem as fonts não há como saber.

A implementação típica (tirada de algum código que já tenho no emacs) seria:

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

… e confiar no programa que sai do escopo para limpar depois.

Se você trabalha em uma plataforma onde a limpeza deve ser feita manualmente, eu provavelmente adicionaria uma rotina de limpeza manual.

Outro problema em fazer isso é que não é seguro para threads. Em um ambiente multithreaded, dois threads poderiam passar pelo “if” antes de ter uma chance de alocar a nova instância (assim, ambos). Isso ainda não é muito grande se você está contando com a rescisão do programa para limpar de qualquer maneira.

Trata-se do gerenciamento de tempo de vida do object. Suponha que você tenha mais do que singletons em seu software. E eles dependem do singleton do Logger. Durante a destruição do aplicativo, suponha que outro object singleton use o Logger para registrar suas etapas de destruição. Você tem que garantir que o Logger deve ser limpo por último. Portanto, confira também este documento: http://www.cs.wustl.edu/~schmidt/PDF/ObjMan.pdf

Eu não encontrei uma implementação CRTP entre as respostas, então aqui está:

 template class Singleton { public: Singleton() = delete; Singleton(const Singleton &) = delete; Singleton &operator=(const Singleton &) = delete; static HeirT &instance() { static HeirT instance; return instance; } }; 

Para usar basta herdar sua class deste, como: class Test : public Singleton

Alguém mencionou std::call_once e std::once_flag ? A maioria das outras abordagens – incluindo bloqueio duplo verificado – está quebrada.

Um grande problema na implementação do padrão singleton é a boot segura. A única maneira segura é proteger a seqüência de boot com barreiras de synchronization. Mas essas barreiras precisam ser iniciadas com segurança. std::once_flag é o mecanismo para obter uma boot segura garantida.

 #define INS(c) private:void operator=(c const&){};public:static c& I(){static c _instance;return _instance;} 

Exemplo:

  class CCtrl { private: CCtrl(void); virtual ~CCtrl(void); public: INS(CCtrl); 

Além da outra discussão aqui, pode ser interessante notar que você pode ter globalidade, sem limitar o uso a uma instância. Por exemplo, considere o caso da referência contando algo …

 struct Store{ std::array data; size_t get(size_t idx){ /* ... */ } void incr_ref(size_t idx){ /* ... */} void decr_ref(size_t idx){ /* ... */} }; template 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 

Agora em algum lugar dentro de uma function (como main ) você pode fazer:

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

Os árbitros não precisam armazenar um ponteiro de volta ao respectivo Store porque essa informação é fornecida em tempo de compilation. Você também não precisa se preocupar com a vida útil da Store , porque o compilador exige que ela seja global. Se realmente existe apenas uma instância de Store , não há sobrecarga nessa abordagem; Com mais de uma instância, cabe ao compilador ser inteligente sobre a geração de código. Se necessário, a class ItemRef pode até ser feita como uma friend da Store (você pode ter amigos modelo!).

Se a própria Store for uma class de modelo, as coisas ficarão confusas, mas ainda é possível usar esse método, talvez implementando uma class auxiliar com a seguinte assinatura:

 template  struct StoreWrapper{ /* stuff to access store_p, eg methods returning instances of ItemRef. */ }; 

O usuário agora pode criar um tipo StoreWrapper (e uma instância global) para cada instância global do Store e sempre acessar os armazenamentos por meio da instância do wrapper (esquecendo os detalhes dos parâmetros do modelo necessários para usar o Store ).

Eu acho que você deve escrever uma function estática em que seu object estático é excluído. Você deve chamar essa function quando estiver prestes a fechar seu aplicativo. Isso garantirá que você não tenha memory leaks.

O documento que foi vinculado acima descreve a deficiência de bloqueio verificado duplamente é que o compilador pode alocar a memory para o object e definir um ponteiro para o endereço da memory alocada, antes que o construtor do object tenha sido chamado. É muito fácil em c ++ no entanto usar alocadores para alocar a memory manualmente e, em seguida, use uma chamada de construção para inicializar a memory. Usando este appraoch, o bloqueio duplo verificado funciona muito bem.

Que tal usar o posicionamento novo assim:

 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; } };