C ++ Синглтон дизайн

Недавно я столкнулся с реализацией / реализацией шаблона проектирования Singleton для C ++. Это было так (я принял его из примера реальной жизни):

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

Из этого объявления я могу вывести, что поле экземпляра инициируется в куче. Это означает, что есть выделение памяти. Для меня совершенно непонятно, когда именно память будет освобождена? Или есть ошибка и утечка памяти? Похоже, что в реализации есть проблема.

Мой главный вопрос: как мне его реализовать правильно?

См. Эту статью для простой конструкции для ленивой оценки с гарантированным разрушением singleton:
Может ли кто-нибудь предоставить мне образец Singleton в c ++?

Классический ленивый оцененный и правильно уничтоженный синглтон.

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

См. Эту статью о том, когда использовать синглтон: (не часто)
Синглтон: как его использовать

См. Две статьи о порядке инициализации и о том, как справиться:
Порядок инициализации статических переменных
Поиск статических задач инициализации C ++

См. Эту статью, описывающую сроки жизни:
Каково время жизни статической переменной в C ++-функции?

См. Эту статью, в которой обсуждаются некоторые streamовые последствия для синглетонов:
Экземпляр Singleton, объявленный как статическая переменная метода GetInstance

См. Эту статью, в которой объясняется, почему двойная проверка блокировки не будет работать на C ++:
Каковы все общие неопределенные типы поведения, о которых должен знать программист на C ++?
Д-р Доббс: C ++ и опасности двойного контроля: Часть I

Являясь Синглтоном, вы обычно не хотите, чтобы его разрушили.

Он будет разорван и отменен, когда программа завершится, что является нормальным желаемым поведением для одноэлементного режима. Если вы хотите явно очистить его, довольно легко добавить статический метод в class, который позволит вам восстановить его в чистое состояние и перераспределить его в следующий раз, когда он будет использоваться, но это выходит за frameworks «classический» синглтон.

Вы могли бы избежать выделения памяти. Существует много вариантов, все из которых имеют проблемы в случае многопоточной среды.

Я предпочитаю такую ​​реализацию (на самом деле, я не правильно сказал, что предпочитаю, потому что я избегаю синглетонов как можно больше):

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

Он не имеет динамического распределения памяти.

Другая альтернатива, не использующая выделение: создайте синглтон, скажем, classа C , как вам нужно:

 singleton() 

с помощью

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

Ни тот, ни ответ Cătălin автоматически не streamобезопасны в текущем C ++, но будут в C ++ 0x.

Ответ @Loki Astari превосходный.

Однако бывают случаи с несколькими статическими объектами, где вы должны быть в состоянии гарантировать, что singleton не будет уничтожен, пока все ваши статические объекты, которые используют singleton, больше не нуждаются в этом.

В этом случае std::shared_ptr может использоваться, чтобы поддерживать singleton для всех пользователей, даже когда статические деструкторы вызываются в конце программы:

 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() {} }; 

Если вы хотите выделить объект в кучу, почему бы не использовать уникальный указатель. Память также будет освобождена, так как мы используем уникальный указатель.

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

Решение в принятом ответе имеет существенный недостаток – деструктор для singleton вызывается после того, как элемент управления выходит из «основной» функции. На самом деле могут быть проблемы, когда некоторые зависимые объекты выделяются внутри «main».

Я столкнулся с этой проблемой при попытке представить Singleton в приложении Qt. Я решил, что все мои диалоги настройки должны быть Singletons, и приняли шаблон выше. К сожалению, основной class Qt «QApplication» был выделен в стеке в «основной» функции, а Qt запрещает создавать / уничтожать диалоги, когда объект приложения недоступен.

Вот почему я предпочитаю выделенные в кучу одиночные точки. Я предоставляю явные методы «init ()» и «term ()» для всех синглтонов и называю их внутри «main». Таким образом, я полностью контролирую порядок создания / уничтожения одиночек, а также гарантирую, что будут созданы одиночные игры, независимо от того, кто-то назвал «getInstance ()» или нет.

Вот простая реализация.

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

Создается только один объект, и эта ссылка на объект возвращается каждый раз после каждого слова.

 SingletonClass instance created! 00915CB8 00915CB8 

Здесь 00915CB8 - это ячейка памяти одноэлементного объекта, то же самое в течение всей программы, но (обычно!) Различна при каждом запуске программы.

NB Это не безопасный stream. Вы должны обеспечить безопасность streamа.

На самом деле это, вероятно, выделено из кучи, но без источников нет возможности узнать.

Типичная реализация (взятая из некоторого кода, который у меня есть в emacs уже):

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

… и полагаться на программу, выходящую из области действия, для очистки после этого.

Если вы работаете на платформе, где очистка должна выполняться вручную, я бы, вероятно, добавил ручную процедуру очистки.

Другая проблема заключается в том, что он не является streamобезопасным. В многопоточной среде два streamа могли бы пройти через «if», прежде чем у них будет возможность выделить новый экземпляр (так и будет). Это все еще не слишком большая сделка, если вы все равно полагаетесь на завершение программы для очистки.

Речь идет об управлении жизненным временем объекта. Предположим, что у вас в вашем программном обеспечении больше, чем однопользовательских. И они зависят от Singleger Logger. Во время уничтожения приложения предположим, что другой одноэлементный объект использует Logger для регистрации шагов его уничтожения. Вы должны гарантировать, что Logger должен быть очищен последним. Поэтому, пожалуйста, также ознакомьтесь с этой статьей: http://www.cs.wustl.edu/~schmidt/PDF/ObjMan.pdf

Я не нашел CRTP-реализации среди ответов, так вот вот:

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

Чтобы просто наследовать свой class, например: class Test : public Singleton

Кто-нибудь упомянул std::call_once и std::once_flag ? Большинство других подходов, включая двойную проверку блокировки, нарушены.

Одной из основных проблем в реализации одноэлементного шаблона является безопасная инициализация. Единственный безопасный способ – защитить последовательность инициализации синхронизирующими барьерами. Но сами эти барьеры должны быть безопасно начаты. std::once_flag – это механизм, гарантирующий безопасную инициализацию.

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

Пример:

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

В дополнение к другому обсуждению здесь, возможно, стоит отметить, что вы можете иметь глобальность, без ограничения использования одного экземпляра. Например, рассмотрим случай ссылки, считая что-то …

 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 

Теперь где-то внутри функции (например, main ) вы можете сделать:

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

В refs нет необходимости хранить указатель обратно в соответствующий Store потому что эта информация предоставляется во время компиляции. Вам также не нужно беспокоиться о жизни Store , потому что компилятор требует, чтобы он был глобальным. Если на самом деле есть только один экземпляр Store , в этом подходе нет накладных расходов; с более чем одним экземпляром компилятор должен уметь разбираться в генерации кода. Если необходимо, class ItemRef можно даже сделать friend Store (у вас могут быть шаблонные друзья!).

Если Store сам является шаблоном, тогда все становится более беспорядочным, но все же можно использовать этот метод, возможно, путем реализации вспомогательного classа со следующей сигнатурой:

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

Теперь пользователь может создать тип StoreWrapper (и глобальный экземпляр) для каждого экземпляра глобального Store и всегда обращаться к хранилищам через свой экземпляр оболочки (таким образом забывая о деталях шаблона параметров шаблона, необходимых для использования Store ).

Я думаю, вы должны написать статическую функцию, в которой ваш статический объект будет удален. Вы должны вызвать эту функцию, когда собираетесь закрыть приложение. Это гарантирует, что у вас не будет утечки памяти.

Документ, который был связан с выше, описывает недостаток двойной проверки блокировки заключается в том, что компилятор может выделить память для объекта и установить указатель на адрес выделенной памяти, прежде чем вызывал конструктор объекта. В c ++ довольно просто использовать распределители для распределения памяти вручную, а затем использовать конструктивный вызов для инициализации памяти. Используя эту оценку, двойная проверка блокировки работает просто отлично.

Как насчет использования нового размещения следующим образом:

 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; } }; 
  • Необходимость волатильного модификатора в двойной проверенной блокировке в .NET.
  • Java Singleton и синхронизация
  • Когда сборщик мусора стирает экземпляр объекта, который использует шаблон Singleton?
  • ASP.NET Singleton
  • Существуют ли жизнеспособные альтернативы шаблону Singleton в GOF?
  • Как избежать нескольких экземпляров формы Windows в c #
  • Каков наилучший подход для использования Enum в качестве одноэлементной на Java?
  • Использование разных загрузчиков classов для разных тестов JUnit?
  • Проблемы с шаблоном Singleton
  • получение соединения db через singleton class
  • Шаблон Singleton в C ++
  • Interesting Posts

    JSON.NET и nHibernate Lazy Загрузка коллекций

    Элементы управления Microsoft Excel ActiveX отключены?

    display: inline vs display: block

    Xcode – профиль iPhone не соответствует какой-либо действительной паре с сертификатом / закрытым ключом в ключевом ключе по умолчанию

    Windows 7 ПК не может найти ПК с Windows Vista

    Как обновить информацию в Android-активности из фоновой службы

    Go – Строки даты / времени, которые не являются стандартными форматами

    Мышь зависает при сканировании или установке программного обеспечения

    Поддерживает ли Google Chrome синхронизацию истории между компьютерами?

    Как установить контроль доступа-allow-origin в webrick под рельсами?

    Согласование нескольких шаблонов

    Улучшенный дизайн для передачи данных в другие виды ng-view и их сохранение через controllerы

    FXMLLoader getController возвращает NULL?

    C ++: “std :: endl” vs “\ n”

    Как разбить множество на два подмножества, так что разница между суммой чисел в двух наборах минимальна?

    Давайте будем гением компьютера.