Дмитрий Федоров - Примеры использования Паттерн Singleton (Одиночка)
- Название:Примеры использования Паттерн Singleton (Одиночка)
- Автор:
- Жанр:
- Издательство:неизвестно
- Год:неизвестен
- ISBN:нет данных
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Дмитрий Федоров - Примеры использования Паттерн Singleton (Одиночка) краткое содержание
Примеры использования Паттерн Singleton (Одиночка) - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
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().
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() {
Singleton *p = SinglImpl::Instance();
…
…
…
p->FreeInst();
}
Результат работы:
~SinglImpl
~Singleton
Иногда может возникнуть ситуация, при которой клиент должен полиморфно работать с объектами, имеющими общий базовый класс, но некоторые из них реализуют паттерн Singleton, а некоторые нет. Проблема возникает в момент освобождения объектов, так как у простых классов нет механизма отслеживания ссылок, а у классов, реализующих Singleton, он есть. При вызове метода FreeInst() через указатель на базовый класс будет вызываться FreeInst() базового класса, не имеющего понятия о подсчете ссылок. Это приведет и к безусловному удалению объектов “Singleton” из памяти. Для предотвращения такого поведения следует объявить виртуальным метод FreeInst() в базовом классе и реализовать специфическое поведение метода для классов Singleton. Реализация FreeInst() в базовом классе предоставляет механизм удаления объектов, не являющихся Singleton’ами.
class base {
protected:
virtual ~base(){}
//гарантируем удаление только через FreeInst()
public:
virtual void Do1()=0;
virtual void FreeInst(){delete this;}
};
class Simple: public base {
protected:
~Simple () {printf("Simple::~Simple\n");}
public:
void Do1(){printf("Simple::Do1\n");}
};
class Singleton: public base {
static Singleton* _self;
static int _refcount;
protected:
Singleton(){}
~Singleton () {printf("Singleton::~Singleton\n");}
public:
static Singleton* Instance() {
if(!_self) _self = new Singleton ();
_refcount++;
return _self;
}
void FreeInst() {_refcount--; if(!_refcount) {delete this; _self=NULL;}}
void Do1(){printf("Singleton::Do1\n");}
};
Singleton* Singleton::_self=NULL;
int Singleton:: _refcount=0;
class Client {
base *objs[2];
int ind;
public:
Client(){
objs[0]=NULL;objs[1]=NULL;ind=0; }
~Client() {
for(int i=0;i FreeInst();
}
void Add(base *p){if(ind<2) objs[ind++]=p;}
void Do() {
for(int i=0;iDo1();
}
};
void main() {
Client cl;
cl.Add(Singleton::Instance());
cl.Add(new Simple());
cl.Do();
}
результат работы программы:
Singleton::Do1 Simple::Do1 Singleton::~Singleton Simple::~Simple
В данном примере при разрушении объект класса Client автоматически вызываются методы FreeInst() для каждого из хранимых указателей. Благодаря тому, что этот метод объявлен виртуальным, а в классах реализующих паттерн Singleton этот метод переопределен с учетом подсчета ссылок, то программа работает именно так как ожидается.
Применение шаблонов языка C++.
Альтернативой приведенной выше реализации может служить реализация класса Singleton при помощи шаблонов языка С++. Преимущество такого подхода заключается в автоматической параметризации метода Instance(), что приводит к отсутствию необходимости переопределять его в классах потомках. По изложенным ранее причинам конструктор класса-потомка также должен быть объявлен защищенным, а деструктор виртуальным. Кроме того, базовый класс Singleton должен быть объявлен другом класса наследника, поскольку метод Instance() базового класса в этой модели создает объект производного класса.
template
class Singleton {
static T* _self;
static int _refcount;
protected:
Singleton(){}
virtual ~Singleton(){_self=NULL;}
public:
static T* Instance();
void FreeInst();
};
template
T* Singleton::_self = NULL;
template
int Singleton::_refcount=0;
template
T* Singleton::Instance() {
if(!_self) _self=new T;
_refcount++;
return _self;
}
template
void Singleton::FreeInst() {
if(--_refcount==0) delete this;
}
class Derived: public Singleton {
protected:
Derived(){}
friend class Singleton;
};
int main(int argc, char* argv[]) {
Derived *p = Derived::Instance();
…
…
…
p->FreeInst();
return 0;
}
Классы, объекты которых должны существовать в единственном экземпляре, просто наследуются от шаблонного класса Singleton. Такой подход, однако, не позволяет создать иерархию классов во главе с классом-интерфейсом, в которой некоторые из классов-наследников реализуют паттерн проектирования Singleton, а некоторые нет (См листинг 8). С другой стороны, применение параметризованного класса позволяет вынести код Singleton’а в отдельный файл и включать его в последствии в создаваемые приложения, обеспечивая тем самым повторное использование паттерна Singleton.
Резюме
Интервал:
Закладка: