16:01 

Трюк 0x0003 — К слову о синглетонах

Куб 0
Классический синглетон — класс-одиночка — представляет собой класс, объект которого может быть создан один единственный раз за весь период выполнения программы, и который существует в течение всего времени выполнения программы. Реализовать его можно разными способами, о которых можно прочитать в литературных трудах Банды Четырех и ставшего притчей во языцех Коплиена.
Однако, наиболее компактным, и не вызывающим головной боли при эксплуатации, является приведенный ниже вариант.

//------------------------------------------------------------
class Service {
private:
Service() {}
Service(const Service &) {}
Service &operator=(const Service &) {}
public:
virtual ~Service() {}
void Method1() {}
void Method2() {}
void Method3() {}
static Service g_Instance;
};
Service Service::g_Instance;
//------------------------------------------------------------
// ниже приведен пример использования
void Func1() {
Service::g_Instance.Method1();
}
void Func2() {
Service::g_Instance.Method2();
}
int main() {
// раскомментируйте приведенные ниже строки
// и убедитесь, что они не работают
// Service local_Service;
// Service another_local_Service(Service::g_Instance);
// Service *ptr; *ptr=Service::g_Instance;
Func1();
Func2();
Service::g_Instance.Method3();
return 0;
}
//------------------------------------------------------------
Предположим, экземпляр класса Service должен быть создан только один единственный раз. Для этого конструкторы делаются защищенными, защищенным делается так же оператор присваивания, а в открытом интерфейсе создается статическая переменная g_Instance (хотя и вопреки общепринятому обычаю включать в открытый интерфейс только методы, но не переменные, чтобы защитить их от несанкционированной модификации), являющаяся экземпляром самого класса. Очевидно, что g_Instance — это единственный случай, когда может быть вызван защищенный конструктор. Так же очевидно, что g_Instance не может быть модифицирована или перезаписана каким-то другим экземпляром класса Service, так как клиент класса лишен возможности самстоятельно вызвать конструктор.



Другим подходом к созданию синглетона является использование шаблона.

//------------------------------------------------------------
template <class T>
class SingletonKit {
protected:
SingletonKit() {}
SingletonKit(const SingletonKit &);
SingletonKit &operator= (const SingletonKit &);
public:
~SingletonKit() {}
static T g_Instance;
};
template<class T> T SingletonKit<T>::g_Instance;
//------------------------------------------------------------
Любой класс можно превратить в синглетон, используя SingletonKit, если соблюдать некоторые правила при написании класса, а именно:
— создать закрытый конструктор, запретить конструктор копирования и присваивание;
— сделать дружественным класс SingletonKit.
Далее приводится пример тестового класса.

//------------------------------------------------------------
class Test {
friend class SingletonKit<Test>; // SingletonKit объявлен дружественным
private:
Test() {
m_Data=-1;
}
Test(const Test &); // запрет копирования
Test &operator= (const Test &); // и присваивания
int m_Data;
public:
~Test() {}
void SetData(int Data) {
m_Data=Data;
}
int GetData() {
return m_Data;
}
};
//------------------------------------------------------------
Теперь объект класса Test можно использовать как синглетон. Единственный объект доступен через SingletonKit<Test>::g_Instance.

//------------------------------------------------------------
int main() {
SingletonKit<Test>::g_Instance.SetData(100);
Test &Ref=SingletonKit<Test>::g_Instance;
int Data=Ref.GetData();
return 0;
}
//------------------------------------------------------------
Применение конструкции с шаблонным классом позволяет "осинглетить" любой класс в уже существующей иерархии классов. Например, если есть иерархия классов WindowDialogWindowStatusWindow, где последний класс написан разработчиком и должен управлять выводом статуса программы, то вполне можно сделать его синглетным, если требуется. Для этого нужно лишь удовлетворить требованиям к объявлению класса.

//------------------------------------------------------------
class StatusWindow: public DialogWindow {
friend class SingletonKit<StatusWindow>; // SingletonKit объявлен дружественным
private:
StatusWindow() {}
StatusWindow(const StatusWindow &); // запрет копирования
StatusWindow &operator= (const StatusWindow &); // и присваивания
public:
~StatusWindow() {}
};
//------------------------------------------------------------

@темы: Паттерны, Трюки с наследованием

URL
   

Записная книжка программиста C++

главная