Здавалка
Главная | Обратная связь

Шаблон Синглетон (Singleton pattern)



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

Иногда бывает очень важно, чтобы класс мог создать только один экземпляр (объект). Например, система может иметь несколько принтеров, но должен быть только один спулер принтера. Как мы можем гарантировать, что имеется только один экземпляр класса, и что этот экземпляр доступен?! Можно, например, объявить глобальную переменную. Однако, данный способ плох тем, что мы во-первых засоряем пространство имен, а во-вторых не можем предотвратить создание нескольких экземпляров класса.

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

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

# include <iostream>using namespace std; class Singleton{ private: // указатель на единственный экземпляр класса static Singleton*s; int k; //закрытый конструктор Singleton(int i){ k = i; } public: //функция для получения указателя на //единственный экземпляр класса static Singleton*getReference(){ return s; } //получение значения нестатического члена класса int getValue(){ return k; } //перезапись значения нестатического члена класса void setValue(int i){ k = i; } }; // Инициализация статического члена класса.Singleton* Singleton::s=new Singleton(3); void main(){ //получение указателя на //единственный экземпляр класса Singleton*p=Singleton::getReference(); //работа с компонентом объекта cout<<p->getValue()<<"\n\n"; p->setValue(p->getValue()+5); cout<<p->getValue()<<"\n\n";}

Комментарии к примеру.

Класс Singleton окончательный — его нельзя расширить.

Его конструктор закрытый — никакой метод не может создать экземпляр этого класса.

Единственный экземпляр s класса Singleton — статический, он создается внутри класса.

Однако можно получить указатель на этот экземпляр методом getReference(), изменить состояние экземпляра s методом setValue() или просмотреть его текущее состояние методом getValue().

Безусловно, это только схема — класс Singleton надо еще наполнить полезным содержимым, но идея выражена ясно и полностью.

Предыдущая Оглавление Следующая
Предыдущая Оглавление Следующая

Домашнее задание

Создайте класс Время, в котором реализованы операции сложения, вычитания, сравнения, ввода и вывод на экран. Возможность конвертации времени из американского формата am (pm): 10:00 pm = 22:00, 12:00 pm =00:00

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

Предыдущая Оглавление Следующая

 


 

Предыдущая Оглавление Следующая

Урок №25.

  • Перегрузка оператора ->
  • Понятие "умного" указателя (smart pointer)
  • Функции с произвольным количеством и типом аргументов.
  • Домашнее задание.
Предыдущая Оглавление Следующая
Предыдущая Оглавление Следующая

Перегрузка оператора ->

Мы надеемся, что вы помните, что в C++ можно перегрузить почти все операторы, за исключением нескольких. Во всяком случае, оператор -> перегружается, и это имеет значение крайне важное. Кстати, этот оператор называется селектором (member selector). Рассмотрим пример:

#include <iostream>using namespace std;// класс, указатель на который// будет инкапсулированclass Temp{ int TEMP; public: //конструктор Temp(){TEMP=25;} //функция показа на экран void TempFunction(){ cout<<"TEMP = "<<TEMP<<"\n\n"; } //функция установки значения void TempSet(int T){ TEMP=T; }}; // класс, инкапсулирующий указательclass MyPtr{ //указатель на класс Temp Temp*ptr; public: //конструктор MyPtr(Temp*p=NULL){ ptr=p; } // Оператор преобразования типа // от инкапсулированного к инкапсулирующему operator Temp*(){ return ptr; }; // Оператор селектора -> // который позволит обратиться // напрямую к "спрятанному" // указателю Temp* operator->(){ return ptr; } //оператор ++ для смещения указателя вперед MyPtr operator++(){ ptr++; return *this; } }; void main (){ //создание нового объекта Temp*main_ptr = new Temp; //простое обращение к членам //объекта через "родной" указатель main_ptr->TempFunction(); //создание объекта класса-указателя MyPtr pTemp(main_ptr); //обращение через класс-указатель pTemp->TempFunction(); //создание массива объектов //инкапсулируемого класса Temp*arr_=new Temp[3]; //заполнение вышеозначенного массива //значениями от 0 до 2 for(int i=0;i<3;i++) arr_[i].TempSet(i); //создание объекта класса указателя //и запись в него адреса массива //(здесь работает преобразование типа) MyPtr arr_temp=arr_; //сдвиг на один элемент вперед arr_temp++; //демонстрация результата arr_temp->TempFunction(); //удаление объектов delete main_ptr; delete[]arr_; } Результат работы программы TEMP = 25 TEMP = 25 TEMP = 1

Итак, обсудим результат: У нас есть класс Temp, который может иметь экземпляры, обладает некоторым членом TEMP и минимальным набором функций для работы с ним. Кроме того, мы создали класс объекта-указателя MyPtr, в котором храним обычный указатель, но доступ к нему ограничиваем, и перегружаем для него операторы:

  1. Оператор приведения типа от Temp к MyPtr
  2. Оператор -> (selector).
  3. Оператор ++, реализующий сдвиг указателя на один шаг вперед.

Рассмотрим положительные моменты - мы получили класс объектов-указателей, которые можно смело применять вместо настоящих. Это удобно, т.к. все функции для работы с указателем можно инкапсулировать в этом классе. Однако есть еще одно широко распространенное применение данной конструкции, с которым мы с вами познакомимся в следующем разделе урока. Вперед!

Предыдущая Оглавление Следующая
Предыдущая Оглавление Следующая

Понятие "умного" указателя (smart pointer)

Начнем с того, что то, о чём мы с Вами сейчас будем говорить, реализуется с помощью перегрузки селектора. Итак.

Умный указатель (англ. smart pointer) — класс, имитирующий интерфейс обычного указателя и добавляющий к нему некую новую функциональность, например проверку границ при доступе или очистку памяти.

Иначе говоря, умные указатели - это объекты, в которых хранится указатель на настоящий объект и, как правило, счётчик числа обращений к объекту.

В классе также присутствует деструктор для настоящего объекта, который не вызывается извне, а только внутри smart pointer. Принцип таков, что, когда счётчик обращений к объекту равен 0, вызовется деструктор.

Например, есть массив указателей на объекты. Вы используете копию указателя на один объект где-нибудь ещё. Потом очищаете массив, удаляя при этом объекты, а "где-нибудь ещё" все ещё хранит указатель. Естественно, что, при доступе к нему происходит сбой программы, т.к. объект был удалён при очистке массива.

При наличии smart pointer, такого не произойдёт, т.к. при создании копии указателя внутренний счётчик увеличится на 1. Те будет равен двум - 1 для массива, и ещё 1 для копии. Теперь даже если мы удалим объект из массива, внутренний счётчик уменьшится на 1 и станет равным 1, т.е. копия может продолжать работать с объектом. После того как он станет ненужным, копия освобождает память от него и счётчик становится равным 0. Именно, в этот момент вызывается деструктор для оригинального объекта.

Ну что ж, всё сказано. Пора попробовать блюдо под названием "умный указатель" на вкус. :)))

#include <iostream>using namespace std;class Temp{ int TEMP; public: //конструктор Temp(){TEMP=25;} //функция показа на экран void TempFunction(){ cout<<"TEMP = "<<TEMP<<"\n\n"; } //функция установки значения void TempSet(int T){ TEMP=T; }};// Класс, реализующий умный указательclass SmartPointer { // Инкапсулированный указатель Temp*ptr; //счётчик копий int count_copy; public: //конструктор SmartPointer (Temp*p=NULL){ //записываем 0 при создании копий нет count_copy=0; ptr=p; } // конструктор копирования SmartPointer (const SmartPointer&obj){ //создается копия - увеличиваем счётчик ptr=obj.ptr; count_copy++; } //перегрузка оператора равно SmartPointer operator=(const SmartPointer&obj){ //создается копия - увеличиваем счётчик ptr=obj.ptr; ptr=obj.ptr; count_copy++; //возвращаем сам объект для ситуации a=b=c return *this; } // уничтожение объекта ~SmartPointer(){ //если объект есть и копий нет if(ptr!=NULL&&count_copy==0){ cout<<"\n~Delete Object\n"; //уничтожаем объект delete[]ptr; } //в противном случае(уничтожается копия) else{ //уменьшаем счётчик count_copy--; cout<<"\n~Delete Copy\n"; } } //старая добрая перегрузка селектора Temp* operator->(){ return ptr; } }; void main(){ //создаем объект Temp*main_ptr=new Temp; //инициализируем этим объектом умный указатель SmartPointer PTR(main_ptr); //проверяем работу умного указателя PTR->TempSet(100); PTR->TempFunction(); // создаем копию (работа конструктора копирования) SmartPointer PTR2=PTR;} Результат работы программы:// работа с объектом через умный указательTEMP = 100//уничтожение копии~Delete Copy//уничтожение самого объекта~Delete Object

Сейчас, мы создали свой собственный указатель, в последующих уроках, нами будет рассмотрен уже готовый smart poiter из библиотеки STL, под названием auto_ptr.

Предыдущая Оглавление Следующая
Предыдущая Оглавление Следующая






©2015 arhivinfo.ru Все права принадлежат авторам размещенных материалов.