class Window {
int width;
int height;
public:
Window() {
Application *p=Application::Instance();
p->loadIniInt(string("Window"), string("width"));
p->loadIniInt(string("Window"), string("height"));
}
~Window() {
Application *p=Application::Instance();
p->saveIniInt(string("Window"), string("width"), width);
p->saveIniInt(string("Window"),string("height"), height);
}
};
Этот листинг показывает, как можно организовать каркас оконного приложения, используя паттерн Singleton. Из класса окна требуется доступ к некоторым функциям объекта Application. Поскольку объект приложения существует всегда в одном экземпляре, то он реализует паттерн Singleton, а доступ к объекту приложения из объекта окна осуществляется благодаря методу Instance().
Проблема удаления объекта “Singleton”.
В приведенной выше реализации класса Singleton, есть метод создания объекта, но отсутствует метод его удаления. Это означает, что программист должен помнить в каком месте программы объект удаляется. Другая проблема, связанная с удалением объекта из памяти, возникает при полиморфном использовании объектов класса. Рассмотрим, например, такой код.
Листинг 5
class Client {
Singleton * _pS;
public:
SetObject(Singleton *p) {_pS=p;}
~Client(){delete _pS;}
};
void main() {
Client c1,c2;
c1.SetObject(Singleton::Instance());
c2.SetObject(Singleton::Instance());
}
Эта программа будет пытаться удалить дважды один и тот же объект, что приведет к исключительной ситуации в программе. При выходе из контекста функции main, сначала будет вызван деструктор объекта c2, который удалит объект класса Singleton, а затем то же самое попытается сделать и деструктор объекта c1. В связи с этим, хотелось бы иметь механизм, позволяющий автоматически отслеживать ссылки на объект класса Singleton, и автоматически удалять его только тогда, когда на объект нет активных ссылок. Для этого используют специальный метод FreeInst(), удаляющий объект только в случае, если активных ссылок на него нет.
Другая задача, которую надо решить – запрет удаления клиентом объекта Singleton посредством оператора delete. Это решается помещением деструктора в секцию protected.Тем самым, клиенту ничего не остается, как использовать пару Instance()/FreeInst() для управления временем жизни объекта.
Листинг 6
class Singleton {
protected:
static Singleton* _self;
static int _refcount;
Singleton(){};
~Singleton(){};
public:
static Singleton* Instance();
void FreeInst() {_refcount--; if(!_refcount) {delete this; _self=NULL;}}
};
В данном примере, в класс Singleton введен счетчик ссылок. Метод FreeInst() вызывает оператор удаления только тогда, когда _refcount равен нулю.
Если существует необходимость наследовать от класса Singleton, то следует придерживаться определенных правил.
Во-первых, класс-наследник должен переопределить метод Instance(), так, чтобы создавать экземпляр производного класса. Если не предполагается, что указатель будет использоваться полиморфно, то можно объявить возвращаемый тип метода Instance() как указатель на класс-наследник, в противном случае, метод Instance() должен возвращать указатель на базовый класс (Singleton).
Во-вторых, в базовом классе деструктор должен быть объявлен как виртуальный: в определенный момент клиент вызывает метод FreeInst для указателя на базовый класс. Поскольку метод FreeInst сводится к оператору delete this, то в случае, если деструктор не виртуальный, будет вызван деструктор базового класса, но не будет вызван деструктор класса-потомка. Чтобы избежать такой ситуации, следует явно объявить деструктор базового класса виртуальным.
В-третьих, конструктор класса-потомка также должен быть объявлен в защищенной секции, чтобы избежать возможности создания объекта класса напрямую, минуя метод Instance().
Листинг 7
class Singleton {
protected:
static Singleton* _self;
static int _refcount;
Singleton(){}
virtual ~Singleton() {printf ("~Singleton\n");}
public:
static Singleton* Instance();
void FreeInst();
};
class SinglImpl: public Singleton {
protected:
SinglImpl(){}
//объявление виртуальным в базовом классе автоматически
//дает виртуальность в производном.
~SinglImpl() {printf ("~SinglImpl\n");}
public:
static Singleton* Instance() {
if(!_self) _self = new SinglImpl();
_refcount++;
return _self;
}
};
void main() {
Читать дальше