Чтение онлайн

ЖАНРЫ

Примеры использования Паттерн Singleton (Одиночка)

Федоров Дима

Шрифт:

Проблема удаления объекта “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 {

 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 ;

Поделиться с друзьями: