Class Singleton; // опережающее объявление 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Class Singleton; // опережающее объявление



Class SingletonDestroyer

{

private:

Singleton* p_instance;

public:

~SingletonDestroyer();

void initialize(Singleton* p);

};

Class Singleton

{

private:

static Singleton* p_instance;

static SingletonDestroyer destroyer;

protected:

Singleton() { }

Singleton(const Singleton&);

Singleton& operator=(Singleton&);

~Singleton() { }

friend class SingletonDestroyer;

public:

static Singleton& getInstance();  

};

// Singleton.cpp

#include "Singleton.h"

Singleton * Singleton::p_instance = 0;

SingletonDestroyer Singleton::destroyer;

SingletonDestroyer::~SingletonDestroyer() {

delete p_instance;

}

void SingletonDestroyer::initialize(Singleton* p) {

p_instance = p;

}

Singleton& Singleton::getInstance() {

if(!p_instance) {

   p_instance = new Singleton();

   destroyer.initialize(p_instance);

}

return *p_instance;

}

Ключевой особенностью этой реализации является наличие класса SingletonDestroyer, предназначенного для автоматического разрушения объекта Singleton.

Класс Singleton имеет статический член SingletonDestroyer, который инициализируется при первом вызове Singleton::getInstance() создаваемым объектом Singleton.

При завершении программы этот объект будет автоматически разрушен деструктором SingletonDestroyer (для этого S ingletonDestroye r объявлен другом класса Singleton).

 

Для предотвращения случайного удаления пользователями объекта класса Singleton, деструктор теперь уже не является общедоступным как ранее. Он объявлен защищенным.

 

/*Использование нескольких взаимозависимых одиночек

 

До сих пор предполагалось, что в программе используется один одиночка либо несколько несвязанных между собой. При использовании взаимосвязанных одиночек появляются новые вопросы:

 

Как гарантировать, что к моменту использования одного одиночки, экземпляр другого зависимого уже создан?

Как обеспечить возможность безопасного использования одного одиночки другим при завершении программы? Другими словами, как гарантировать, что в момент разрушения первого одиночки в его деструкторе еще возможно использование второго зависимого одиночки (то есть второй одиночка к этому моменту еще не разрушен)?

Управлять порядком создания одиночек относительно просто. Следующий код демонстрирует один из возможных методов.

 

// Singleton.h

Class Singleton1

{

private:

Singleton1() { }

Singleton1(const Singleton1&);

Singleton1& operator=(Singleton1&);

public:

static Singleton1& getInstance() {

   static Singleton1 instance;

   return instance;

}

};

Class Singleton2

{

private:

Singleton2(Singleton1& instance): s1(instance) { }

Singleton2(const Singleton2&);

Singleton2& operator=(Singleton2&);

Singleton1& s1;

public:

static Singleton2& getInstance() {

   static Singleton2 instance(Singleton1::getInstance());

   return instance;

}

};

// main.cpp

#include "Singleton.h"

Int main()

{

Singleton2& s = Singleton2::getInstance();

return 0;

}

Объект Singleton1 гарантированно инициализируется раньше объекта Singleton2, так как в момент создания объекта Singleton2 происходит вызов Singleton1::getInstance().

 

Гораздо сложнее управлять временем жизни одиночек. Существует несколько способов это сделать, каждый из них обладает своими достоинствами и недостатками и заслуживают отдельного рассмотрения. Обсуждение этой непростой темы остается за рамками лекции.

*/

Несмотря на кажущуюся простоту паттерна Singleton (используется всего один класс), его реализация не является тривиальной.

 

Например, если у нас не однопоточное приложение могу возникнуть гонки потоков.  

При обращении к Singleton гонки связаны с тем, что если объект еще не был создан и 2 потока одновременно выполнят операцию instance, то может быть создано несколько одиночек.

 

SimpleClass* SimpleClass::instance() { // 1

if(m_pinstance == 0) // 2

m_pinstance = new SimpleClass; // 3

return m_pinstance; // 4

} // 5

Так, например, первый поток может выполнить проверку условия из второй строки и быть прерван во время выполнения конструктора.

Пока первый поток отдыхает, начинается работать второй поток, который выполняет тот же код, но в связи с тем, что первый поток не выполнял инициализации m_pinstance, второй поток также вызывает конструктор SimpleClass и возвращает указатель на созданный объект.

После этого может быть активирован первый поток, который заканчивает создание своего экземпляра объекта и повторно инициализирует m_pinstance.

 

Для решения проблемы гонок можно использовать средства синхронизации потоков (мьютексы), которые в настоящее время включены в стандартную библиотеку.

 

static SimpleClass* SimpleClass::instance() {

static QMutex mutex;

if (!m_pinstance) {

mutex.lock();

if (!m_pinstance)

m_pinstance = new SimpleClass;

mutex.unlock();

}

return m_pinstance;

}

С другой стороны, у реализации Singleton, использующей статическую локальную переменную метода instance, состояния гонок потоков возникнуть не может. Это гарантируется стандартом (цитата относится к инициализации локальных статических переменных):

 



Поделиться:


Последнее изменение этой страницы: 2021-11-27; просмотров: 74; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.217.144.32 (0.009 с.)