|
||||
|
Проблема наследованияЕсли существует необходимость наследовать от класса Singleton, то следует придерживаться определенных правил. Во-первых, класс-наследник должен переопределить метод Instance(), так, чтобы создавать экземпляр производного класса. Если не предполагается, что указатель будет использоваться полиморфно, то можно объявить возвращаемый тип метода Instance() как указатель на класс-наследник, в противном случае, метод Instance() должен возвращать указатель на базовый класс (Singleton). Во-вторых, в базовом классе деструктор должен быть объявлен как виртуальный: в определенный момент клиент вызывает метод FreeInst для указателя на базовый класс. Поскольку метод FreeInst сводится к оператору delete this, то в случае, если деструктор не виртуальный, будет вызван деструктор базового класса, но не будет вызван деструктор класса-потомка. Чтобы избежать такой ситуации, следует явно объявить деструктор базового класса виртуальным. В-третьих, конструктор класса-потомка также должен быть объявлен в защищенной секции, чтобы избежать возможности создания объекта класса напрямую, минуя метод Instance(). Листинг 7class 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’ами. Листинг 8class 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<ind;i++) objs[i]->FreeInst(); } void Add(base *p){if(ind<2) objs[ind++]=p;} void Do() { for(int i=0;i<ind;i++) objs[i]->Do1(); } }; 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 этот метод переопределен с учетом подсчета ссылок, то программа работает именно так как ожидается. |
|
||
Главная | В избранное | Наш E-MAIL | Прислать материал | Нашёл ошибку | Наверх |
||||
|