Анимация
JavaScript
|
Главная Библионтека следует предпринять, если приложение нарушает правила языка С++, определяюшие продолжительность жизни синглтона. • Если синглтон нужно уничтожить, следуя правилам языка С++, стратегия Lifetime применяет механизм, аналогичный функции atexit. • Для феникса стратегия Lifetime продолжает использовать механизм, аналогичный функции atexit, но допускает восстановление синглтона. • Для синглтона с заданной продолжительностью жизни стратегия Lifetime вызывает функцию SetLongevity, описанную в разделах 6.7 и 6.8. • При неограниченной продолжительности жизни синглтона стратегия Lifetime не предпринимает никаких действий. В заключение отметим, что стратегия Lifetime содержит две функции: Schedule-Destruction, задающую время уничтожения объекта, и OnDeadReference, регламентирующую поведение программы при обнаружении висячей ссылки. Предположим, что стратегия Lifetime реализуется классом Lifetime<T>. Тогда имеют смысл следующие выражения. void (*pDestructionFunction)О; Lifetime<T>::ScheduleDestructionCpDestructionFunction); Lifetime<T>::OnDeadReferenceO; Функция-член ScheduleDestruction получает указатель на функцию, уничтожающую объект. Таким образом, стратегию Lifetime можно использовать вместе со стратегией Creation. Не забывайте, что стратегия Lifetime не связана с методами уничтожения объектов, которые относятся к стратегии Creation. Единственное предназначение стратегии Lifetime - определять, когда объект должен быть уничтожен. Функция OnDeadReference генерирует исключительные ситуации всегда, за исключением ситуаций, в которых задействован феникс. В последнем случае стратегия не предусматривает никаких действий. Стратегия ThreadingModel описана в приложении. Класс SingletonHolder поддерживает блокировку только на уровне классов, но не на уровне объектов. По этой причине в любой момент времени существует лишь один синглтон. 6.10.3. Сборка класса SingletonHolder Начнем определение шаблонного класса SingletonHolder. Как указывалось в главе 1, для каждой стратегии предназначен отдельный шаблонный параметр. Кроме того, мы предусмотрим шаблонный параметр т, определяющий разновидность синглтона. Шаблонный класс SingletonHolder сам по себе не определяет синглтон. Он лишь задает его поведение и способы управления им с помощью уже существующего класса singleton. template < class т, template <class> class CreationPolicy = CreateusingNew, template <class> class LifetimePolicy = DeafaultLifetime, template <class> class ThreadingModel = SingleThreaded > class SingletonHolder { public: static T& InstanceO; private: вспомогательные функции static void DestroingsingletonC); Защита SingletonHolderС) ; Данные typedef ThreadingModel<т>::volatileType instanceType; static InstanceType* plnstance ; static bool destroyed.; Вопреки ожиданиям, переменная экземпляра не относится к типу Т*. На самом деле она имеет тип ThreadingMode1<T>::vo1ati1eType*. Тип ThreadingModel <т>: : Vol atileType расширяет тип т или тип volatile т в зависимости от фактически применяемой модели потоков. Спецификатор volatile применяется к типам для того, чтобы сообщить компилятору, что значение данного типа может изменяться несколькими потоками. Зная это, компилятор не станет выполнять некоторые виды оптимизации (например, запись значений во внутренние регистры), поскольку это может привести к ошибочной работе потоков. В целях безопасности можно было бы объявить переменную plnstance с типом volatile т*. Это сработает при использовании многопотокового кода (этот факт следует предварительно проверить по документации), но бесполезно в программе с одним потоком. С другой стороны, в модели с одним потоком следует стремиться к оптимизации программы, так что тип т* был бы для переменной plnstance наилучшим выбором. По этой причине тип для переменной plnstance выбирается стратегией ThreadingModel. Если эта стратегия является однопоточной, определяется тип vol atileType. template <c1ass т> class singleThreaded public: typedef T volatileType; Многопоточная стратегия связывала бы параметр т с типом volatile. Детали многопоточных моделей описаны в приложении. Определим теперь функцию-член Instance, в которой объединяются все три стратегии. template <...> т& Sing1etonHo1der<...>::lnstanceC) { if C!plnstance ) { typename ThreadingMode1<T>::Lock guard; if (!plnstance ) if (destroyed.) { LifetimePo1icy<T>::OnDeadReference(); destroyed. = false; pinstance. = CreationPo1icy<T>::Create(); LifetimePo1icy<T>::ScheduleCal1(&Destroysingleton); return *plnstance ; функция instance является единственным открытым членом класса SingletonHolder. Она представляет собой оболочку классов CreationPolicy, LifetimePolicy и ThreadingModel. Класс ThreadingModel <т> содержит внутренний класс Lock. На протяжении жизни объекта класса Lock все другие потоки, пытающиеся создать объект этого типа, блокируются. (Детали описаны в приложении.) Функция DestroySingleton просто разрушает объект класса singleton, очищает занягую память и присваивает переменной destroyed значение true. Класс SingletonHolder никогда не вызывает функцию DestroySingleton, а лишь передает ее адрес функции-члену LifetimePolicy<T>:;ScheduleDestruction. template <...> void SingletonHolder<...>::DestroySingleton() { assert(!destroyed ); CreationPolicy<T>::Destroy(pinstance ); plnstance = 0; destroyed. = true; Класс SingletonHolder передает переменную pinstance и адрес функции DestroySingleton классу LifetimePolicy<T>, предоставляя ему информацию о поведении объекта: подчиняется он правилам языка С++ или является фениксом, имеет заданную продолжительность жизни или бессмертен. 1. Правила С++. Функция LifetimePolicy<T>: :ScheduleDesctruction вызывает функцию atexit, передавая ей адрес функции DestroySingleton. Функция OnDeadReference генерирует исключительную ситуацию std:: 1 ogic error. 2. Феникс. Совпадает с предыдущей стратегией, только функция OnDeadReference не генерирует исключительную ситуацию. Поток управления класса SingletonHolder продолжает выполнение программы и воссоздает объект вновь. 3. Синглтон с заданной продолжительностью жизни. Функция LifetimePo-licy<T>: :ScheduleDesctruction вызывает функцию SetLongevi ty(getLongevi ty(pinstance)). 4. Бессмертный синглтон. Функция LifetimePolicy<T>: :ScheduleDesctruction не выполняет никаких действий- Класс si ngl etonHol de г решает проблему висячей ссылки в соответствии со стратегией LifetimePolicy. Она очень проста: если функция SinlgetonHolder: :instance обнаруживает висячую ссылку, она вызывает функцию LifetimePolicy: :OnDeadReference. Если функция OnDeadReference возвращает управление, функция instance воссоздает новый экземпляр. В заключение функция OnDeadReference должна сгенерировать исключительную ситуацию или прекратить выполнение программы, если синглтон не является фениксом. Вот и вся реализация класса SingletonHolder. Разумеется, теперь основная часть работы перекладывается на три стратегии. 6.10.4. Реализации стратегий Разложить класс на стратегии трудно, зато потом их легко реализовывать. Рассмотрим классы, реализующие стратегии для распространенных разновидностей синглтонов. В табл. 6.1 приведены классы стратегий для класса SingletonHolder. Классы стратегий, выделенные курсивом, являются шаблонными параметрами, задаваемыми по умолчанию. 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 [ 52 ] 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |