C++
Шаблон дизайна Singleton
Поиск…
замечания
Синглтон предназначен для обеспечения того, чтобы класс имел только один экземпляр и обеспечивал глобальную точку доступа к нему. Если вам нужен только один экземпляр или удобная глобальная точка доступа, но не оба, рассмотрите другие варианты, прежде чем обращаться к синглтону.
Глобальные переменные могут затруднить использование кода. Например, если одна из вызывающих функций недовольна данными, полученными ею от Singleton, вам нужно сначала отследить, что сначала дает синглтонные плохие данные.
Синглтоны также поощряют сцепление , термин, используемый для описания двух компонентов кода, которые объединены вместе, таким образом уменьшая каждый компонент собственной мерой самоограничения.
Синглтоны не совместимы с параллелизмом. Когда класс имеет глобальную точку доступа, каждый поток имеет доступ к нему, что может привести к взаимоблокировкам и условиям гонки.
Наконец, ленивая инициализация может вызвать проблемы с производительностью при инициализации в неподходящее время. Удаление ленивой инициализации также устраняет некоторые функции, которые делают особенно интересным Singleton, например полиморфизм (см. Подкласс).
Источники: Шаблоны программирования игр Роберта Нистрома
Ленивая инициализация
Этот пример был снят с раздела Q & A
: http://stackoverflow.com/a/1008289/3807729
См. Эту статью для простой конструкции для ленивой оценки с гарантированным разрушением 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
// ========
// Dont 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 ++-функции?
См. Эту статью, в которой обсуждаются некоторые потоковые последствия для синглетонов:
Экземпляр Singleton, объявленный как статическая переменная метода GetInstance
См. Эту статью, в которой объясняется, почему двойная проверка блокировки не будет работать на C ++:
Каковы все общие неопределенные типы поведения, о которых должен знать программист на C ++?
Подклассы
class API
{
public:
static API& instance();
virtual ~API() {}
virtual const char* func1() = 0;
virtual void func2() = 0;
protected:
API() {}
API(const API&) = delete;
API& operator=(const API&) = delete;
};
class WindowsAPI : public API
{
public:
virtual const char* func1() override { /* Windows code */ }
virtual void func2() override { /* Windows code */ }
};
class LinuxAPI : public API
{
public:
virtual const char* func1() override { /* Linux code */ }
virtual void func2() override { /* Linux code */ }
};
API& API::instance() {
#if PLATFORM == WIN32
static WindowsAPI instance;
#elif PLATFORM = LINUX
static LinuxAPI instance;
#endif
return instance;
}
В этом примере простой коммутатор компилятора привязывает класс API
к соответствующему подклассу. Таким образом, API
может быть доступен без привязки к коду конкретной платформы.
Нитевидный Singeton
Стандарты C ++ 11 гарантируют, что инициализация объектов области функций инициализируется синхронно. Это можно использовать для реализации потокобезопасного синглета с ленивой инициализацией .
class Foo
{
public:
static Foo& instance()
{
static Foo inst;
return inst;
}
private:
Foo() {}
Foo(const Foo&) = delete;
Foo& operator =(const Foo&) = delete;
};
Статический деинициализационно-безопасный синглтон.
Бывают случаи с несколькими статическими объектами, где вы должны быть в состоянии гарантировать, что синглтон не будет уничтожен, пока все статические объекты, которые используют синглтон, больше не нуждаются в нем.
В этом случае std::shared_ptr
может использоваться, чтобы поддерживать singleton для всех пользователей, даже когда статические деструкторы вызываются в конце программы:
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() {}
};
ПРИМЕЧАНИЕ. Этот пример появляется в ответе в разделе вопросов и ответов.