русс | укр

Мови програмуванняВідео уроки php mysqlПаскальСіАсемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование


Linux Unix Алгоритмічні мови Архітектура мікроконтролерів Введення в розробку розподілених інформаційних систем Дискретна математика Інформаційне обслуговування користувачів Інформація та моделювання в управлінні виробництвом Комп'ютерна графіка Лекції


Реалізація патерну Singleton


Дата додавання: 2014-10-07; переглядів: 974.


Класична реалізація Singleton

Розглянемо найбільш часто зустрічається реалізацію патерну Singleton.

// Singleton.h

class Singleton

{

private:

static Singleton * p_instance;

// Конструкторы и оператор присваивания недоступны клиентам

Singleton() {}

Singleton( const Singleton& );

Singleton& operator=( Singleton& );

public:

static Singleton * getInstance() {

if(!p_instance)

p_instance = new Singleton();

return p_instance;

}

};

 

// Singleton.cpp

#include "Singleton.h"

 

Singleton* Singleton::p_instance = 0;

 

Клієнти запитують єдиний об'єкт класу через статичну функцію-член getInstance (), яка при першому запиті динамічно виділяє пам'ять під цей об'єкт і потім повертає покажчик на цю ділянку пам'яті. Впоследcтвіі клієнти повинні самі подбати про звільнення пам'яті за допомогою оператора delete.

Остання особливість є серйозним недоліком класичної реалізації шаблону Singleton. Так як клас сам контролює створення єдиного об'єкта, було б логічним покласти на нього відповідальність і за руйнування об'єкта. Цей недолік відсутній в реалізації Singleton, вперше запропонованої Скоттом Мейерс.

// Singleton.h

class Singleton

{

private:

Singleton() {}

Singleton( const Singleton&);

Singleton& operator=( Singleton& );

public:

static Singleton& getInstance() {

static Singleton instance;

return instance;

}

};

 

Усередині getInstance() використовується статичний екземпляр потрібного класу. Стандарт мови програмування C + + гарантує автоматичне знищення статичних об'єктів при завершенні програми. Дострокового знищення і не потрібно, тому що об'єкти Singleton зазвичай є довгоживучими об'єктами. Статична функція-член getInstance() повертає НЕ покажчик, а посилання на цей об'єкт, тим самим, ускладнюючи можливість помилкового звільнення пам'яті клієнтами.

Наведена реалізація патерну Singleton використовує так звану відкладену ініціалізацію (lazy initialization) об'єкта, коли об'єкт класу инициализируется ні при старті програми, а при першому виклику getInstance(). В даному випадку це забезпечується тим, що статична змінна instance оголошена всередині функції - члена класу getInstance(), а не як статичний член даних цього класу. Відкладену ініціалізацію, в першу чергу, має сенс використовувати в тих випадках, коли ініціалізація об'єкта являє собою дорогу операцію і не завжди використовується.

На жаль, у реалізації Мейерс є недоліки: складність створення об'єктів похідних класів і неможливість безпечного доступу декількох клієнтів до єдиного об'єкту в багатопотокової середовищі.

Покращена версія класичної реалізації Singleton

З урахуванням усього вищесказаного класична реалізація патерну Singleton може бути поліпшена.

 

// Singleton.h

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 (для цього SingletonDestroyer оголошений іншому класу 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 | Результати застосування патерну Singleton


Онлайн система числення Калькулятор онлайн звичайний Науковий калькулятор онлайн