> 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’ами.
Листинг 8
>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;iFreeInst();
> }
> 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() базового класса в этой модели создает объект производного класса.
листинг 9
>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;