Примеры использования Паттерн Singleton (Одиночка) | страница 3
Проблема удаления объекта “Singleton”.
В приведенной выше реализации класса Singleton, есть метод создания объекта, но отсутствует метод его удаления. Это означает, что программист должен помнить в каком месте программы объект удаляется. Другая проблема, связанная с удалением объекта из памяти, возникает при полиморфном использовании объектов класса. Рассмотрим, например, такой код.
>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() для управления временем жизни объекта.
>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, то в случае, если деструктор не виртуальный, будет вызван деструктор базового класса, но не будет вызван деструктор класса-потомка. Чтобы избежать такой ситуации, следует явно объявить деструктор базового класса виртуальным.