Синглтон - это паттерн проектирования предназначенный для создания типа, объект которого могут существовать только в единственном экземпляре на всем протяжении работы приложения.
В C++ это осуществляется при помощи закрытых конструктора по умолчанию, копирующего конструктора, а также оператора = ( operator= ). Синглтон может требовать инициализации или инициализироваться при первом к нему обращении.
Классический синглтон первого типа выглядит так:
// header file
class SomeSingleton
{
public:
static SomeSingleton& Instance();
static void Init(const std::string &file_name);
static void Destroy();
void Foo();
private:
SomeSingleton();
SomeSingleton(const SomeSingleton&) {}
SomeSingleton& operator=(const SomeSingleton&) {}
~SomeSingleton() {}
static SomeSingleton* instance;
void LoadXML(const std::string &file_name);
};
// cpp file
#include "somesingleton.h"
SomeSingleton* SomeSingleton::instance = nullptr; // инстанцирование статического члена
SomeSingleton& SomeSingleton::Instance()
{
return *instance;
}
void SomeSingleton::Init(const std::string &file_name)
{
delete instance;
instance = new SomeSingleton();
instance->LoadXML(file_name);
}
void SomeSingleton::Destroy()
{
delete instance;
instance = nullptr;
}
void SomeSingleton::LoadXML(const std::string &file_name)
{
// Загрузка данных
}
void SomeSingleton::Foo()
{
// Какая-то работа
}
Как видно, объект типа SomeSingleton является статическим приватным членом самого типа. Таким образом, пользователям не надо беспокоиться о передаче указателя на объект этого типа, ибо объект, по сути, инстанцирован в самом типе.
Работа с объектом производится через статическую функцию Instance() которая возвращает ссылку на объект.
Пример работы с этим синглтоном:
// Где-то в начале программы
SomeSingleton::Init("data.xml");
// В любом месте программы
SomeSingleton::Instance().Foo();
// Где-то в конце програмы
SomeSingleton::Destroy();
Для того, чтобы пользователей не беспокоила семантика синглтона основные его методы желательно обернуть inline функциями, например так:
inline void SomeSingleFoo()
{
SomeSingleton::Instance().Foo();
}
Данный синглтон не совсем удобен, поскольку требует инициализации перед работой с ним. Однако, бывают случаи когда это необходимо. Необходимость может быть вызвана например работой в отдельном thread'е, а так-же если в качестве параметра синглтону передаются данные из другого синглтона. В остальных случаях, лучше применить синглтон не требующий инициализации, придумал его Скотт Мейерс. Выглядит он следующим образом:
// header file
class MrsSingleton
{
public:
static MrsSingleton& Instance();
void Foo();
private:
MrsSingleton();
MrsSingleton(const MrsSingleton&) {}
MrsSingleton& operator=(const MrsSingleton&) {}
~MrsSingleton() {}
};
// cpp file
MrsSingleton& MrsSingleton::Instance()
{
static MrsSingleton obj;
return obj;
}
void MrsSingleton::Foo()
{
// Некая работа
}
Работа с этим классом аналогична работе с предыдущим синглтоном, за исключением отсутствия необходимости его инициализации. Т. е. в любом месте программы достаточно написать MrsSingleton::Instance().Foo();
Синглтон - это паттерн предназначенный прежде всего для упрощения работы с объектом и применим там где действительно нужен только один объект некого типа. Не стоит применять синглтоны для выражения бизнес логики приложения, это более низкоуровневый инструмент. Ниша синглтонов - фабрики объектов, класс локализации и т. п.
В качестве примера оправданного применения синглтона в приложении я привел класс Logger предназначенный для записи сообщений в лог.
Ссылки по теме:
http://insidecpp.ru/patterns/singleton/
Книги:
Современное проектирование на C++ Глава 6.