А. Александреску - Современное проектирование на C++ (1119444), страница 42
Текст из файла (страница 42)
В целях безопасности можно было бы объявить переменную р1пзсапсе с типом чо1ас)1е тгч Это сработает при использовании многопотокового кода (этот факт следует предварительно проверить по документации), но бесполезно в программе с одним потоком. С другой стороны, в модели с одним потоком следует стремиться к оптимизации программы. так что тип т* был бы для переменной рспзсапсе наилучшим выбором. По этой причине тип для переменной рспзсапсе выбирается стратегией тЬгеаомпцмоде1.
Если эта стратегия является однопоточной, определяется тип чо1ас11етуре. сещр1асе <с1ааа т> с1авв втпд1етйгеадед ( риЬ11с: суреоег т чо1ас11етуре; Многопоточная стратегия связывала бы параметр т с типом чо1асз 1е. Детали многопоточных моделей описаны в приложении. Определим теперь функцию-член 1пзСапсе, в которой обьединяются все три стратегии. севр1асе <...> то 5)пц1есопно1дег<...>::1пэтапсеО ( 1Г (!р1пзСапсе ) ( сурепаие тйгеаН пцмоде1<т>: г воск цоаго; 1Г (!р1пзСапсе ) ( тг (деэсгоуей ) ( с)гес1иеяо1т су<т>:: опоеабяетегепсеО; дезсгоуед = га1зе; р1пзсапсе = СгеаС(опро11су<т>:гбгеаСеО; ьзгес(веяо11су<т>::всйебо1еса11(епезсгоувт'пд1есоп); гесцгп *р1пзсапсе ; 173 Глава б.
реализация шаблона 81пц!есоп Функция Хпатапсе является единственным открытым членом класса 5зпд1есопно1дег. Она представляет собой оболочку классов сгеас1опро!зсу, с1бес1неро11су я т)згеад1пдмоде1. Класс т)згеад1пдмоде1<т> содержит внутренний класс сос~. На протяжении жизни объекта класса соей все другие потоки, пытающиеся создать обьект этого типа, блокируются.
(Детали описаны в приложении.) Функция оезсгоу5зпд1есоп просто разрушает объект класса 5зпд1есоп, очищает занятую память и присваивает переменной дезсгоуед значение сгце. Класс 5зпд1есопно1дег никогда не вызывает функцию оезсгоу5з пд1есоп, а лишь передает ее адрес функции-члену ь1бес1яеро1з су<т>:: 5с)зедц1еоезс гцссз оп. сеар1асе <...> чозд 5зпд1еСопио1дег<...>::ОезСгоу5зпд1етопо аваегС(!дезСгоуед )," сгеас1опро11су<т>::оезсгоу(рспзсапсе )„. рспзсапсе = о; дезсгоуед = сгце; Класс 51пд1есопно1дег передает переменную рспзсапсе и адрес функции оезсгоу51пд1есоп классу с1Хес1яеро11су<т>, предоставляя ему информацию о поведении объекта; подчиняется он правилам языка С++ или является фениксом, имеет заданную продолжительность жизни или бессмертен.
К Правила С++. Функция с1рес1меро11су<т>::5сЬедц1еоезссгцсс1оп вызывает функцию асехз с, передавая ей адрес функции оезсгоу51пд1есоп. Функция опоеадяетегепсе генерирует исключительную ситуацию зсд::1од1с еггог. 2. Феникс. Совпадает с предыдущей стратегией, только функция опоеадяеГегепсе не генерирует исключительную ситуацию.
Поток управления класса 5зпд1есопно1дег продолжает выполнение программы и воссоздает объект вновь. 3. Синглтан с заданной продолжительностью жизни. Функция ь1Гес1меро1з су<т>:: 5спедц1еоезссгцсс1оп вызывает функцию 5ессопдеч1су(дессопдеч1су(рспзсапсе)). 4. Бессмертный синглтан. Функция ь1рес1неро11су<т>:: 5спедц1еоезссгцсс1оп ' не выполняет никаких действий. Класс 5зчд1 есопно1дег решает проблему висячей ссылки в соответствии со стратегией ьдгес1веро11су.
Она очень проста: если функция 51п1десопно1дег::спасапсе обнаруживает висячую ссылку, она вызывает функцию с1рес1меро11су::опоеадяеУегепсе. Если функция опоеадяетегепсе возвращает упраюение, функция спзсапсе воссоздает новый экземпляр. В заключение функция опоеадяерегепсе должна сгенерировать искчючительную ситуацию или прекратить выполнение программы, если сингтгон не является фениксом. Вот и вся реализация класса 5зпд1есопно1дег. Разумеется, теперь основная часть работы перекладывается на три стратегии. 6. 10.4.
Реализации стратегий Разложить класс на стратегии трудно, зато потом их легко реализовывать. Рассмотрим классы, реализующие стратегии для распространенных разновилностей синглтонов. В табл, 6.1 приведены классы стратегий для класса 51пд1есопно1дег. Классы стратегий, выделенные курсивом, являются шаблонными параметрами, задаваемыми по умолчанию.
174 Часть )й Компоненты таблица 6.1. Стратегии класса 8!пд1етопНо!дог Стратегия Шаблонный класс Комментарии сгеатзоп сгелгеилупдвеи Создает объект с помошью оператора пеи и конструктора по умолчанию сгеатеца1пдма11ос Создает объект с помошью функции зтд::ва11ос и конструктора по умолчанию сгеате5тат!с ое~аи7ШГег4ие Создает объект в статической памяти ьдгетзве Управляет продолжительностью жизни объекта в соответствии с правилами языка С++.
Для выполнения задания вызывает функцию атехзт Выполняет то же самое, что и класс оегац1тс(гет1ее, но допускает воссоздание объекта класса 51пд1етоп р!зоеп1х51пд1етоп 51пд1етопи1тпьопдеу1ту Задает продолжительность жизни объекта класса 51пд1етоп. Предполагается, что в пространстве имен сушестаует функция оетьопдеч1ту, которая, будучи вызванной с параметром ртпзтапсе, возврашает прололжительность жизни синглтона Не лопускает уничтожения объекта класса 51пд1етоп Детали описаны в приложении нооезтгоу т!згеаддпд- дупд7еЯгеадед моде1 с1аззьече1ьоскаЫе Осталось только узнать, как использовать И расширять этот маленький, но довольно мошный шаблонный класс 51пд1етопио1дег. 175 Глава б.
Реализация шаблона 8!пд!е!оп 6.11. Работа с классом 8! пя!е1опНосПег Шаблонный класс 51пд1етопно1дег не требует от приложения специфических функциональных возможностей. Он просто предоставляет специальные средства лля работы с синглтонами в других классах — в нашем коде они обозначены буквой т. Класс т мы будем называть клиеняским (с11еп! с1азз). В клиентском классе следует предусмотреть все меры для предотврашения непреднамеренного создания и уничтожения объектов: конструктор по умолчанию, конструктор копирования, оператор присваивания, деструктор и оператор взятия адреса должны быль закрытыми. Лреллриняв эти меры, нужно объявить дружественным класс сгеатог.
Превентивные меры и объявление гг1епд — вот и все изменения, которые необходимо внести в класс, работаюший с классом 51пд1етопно1дег. Отметим, что все эти изменения совершенно не являются обязательными и представляют собой компромисс между неудобсгвами настройки сушествуюшего кола и риском возникновения фиктивных объектов. Проектные решения, касаюшиеся работы со специальной реализацией класса 51пд1етоп, отражаются в определении типа, как показано ниже. Передавая признаки и опции при вызове некоторой функции, вы передаете их определению типа, выбирая соответствуюшсе поведение объектов. с1азз А ( гуредет 51пц1егопно1дег<А, Сгеасеиззпциеи> 5зпц1еА; // теперь можно использовать функцию 51пд1еА::хпзсапсе() Описать синглтон, возврашаюший объект производного класса, довольно просто. Для этого достаточно внести изменения в класс стратегии Сгеасог.
с1аззА( ... ); с1ааа оегзчед: риЫзс А ( ... ); сеюр1аге <с1азз т> зсгисс муСгеагог : риЫ)с Сгеагеиз1пцнеш<т> ( згасзс т* сгеагеО ( гегигп пеи оег)чед; суредет 5т'пд)есопно1дег<А, 5гаг)сА11осагог, мусгеасог> 5зпд)еА; Кроме того, можно задать параметры конструктора, использовать другую стратегию распределения памяти или настроить класс 5)пц1есоп на каждую отдельную стратегию. Это позволяет точно настроить класс 5зпц1есоп, сохранив все его функциональные возможности, предусмотренные по умолчанию.
Класс стратегии 5(пд1есопш)сЬьопдечзсу полагается на определение функции сесгопдечзгу, находяшейся в пространстве имен. Ее определение может выглядеть следуюшим образом. 1п1)пе ипз1цпед )пс сеггопдеч1су(А*) ( гесигп 5; Это нужно, только если вы используете класс 51пд1еи1сЬгопдеч1гу в определении типа 5зпц1еА. Наши экспериментальные реализации испытывались на задаче КО(.. Теперь она решается с помошью шаблонного класса 51пц1есоп.
Разумеется, эти определения должны находиться в соответствуюших заголовочных файлах. с1ааа кеуЬоагдхюр1 ( с1азз О)зр1аусшр1 ( с1аьа гоцхшр1 ( ... ); )п1зпе ипз1дпед )пс бесгопдеч1су(кеуЬоагдтюр1') ( гесигп Х; ) )п1)пе ипз1цпед )пс 6есгопцеч)су(о1зр1аухюр1*) ( гесигп Х; ) // объекты класс гоц живут дольше )п1)пе ипззцпед )пс йесгопцеч)су(ьоцхюр1*) ( гесигп <; суредет 5)пд1есопно1дег<кеуЬоагдХюр1, 5зпц1есопш)гЬгопцеч)су> кеуЬоагд; суредет 51пц!егопно1дег<о1зр1ауХюр1, 5зпц1есопш)сЬ|опцеч1гу> о)зр1ау; суредет 5зпд1егопно1дег<годхюр1, 51пд1есопМХЬгопцечзгу> год; Учитывая сложность задачи, описанное решение является достаточно простым и очевидным.
6.12. Резюме В начале главы была описана наиболее популярная реализация класса 51пц1егоп на языке С++. Предотвратить тиражирование синглтонов довольно просто, поскольку 176 Часть й. Компоненты для этого сушествует хорошая языковая поддержка. Сложнее всего управлять продолжительностью жизни синглтона„особенно процессом его уничтожения. распознать попытку доступа к уничтоженному синглтону просто. для этого не требуются дополнительные ресурсы. Это распознавание должно быть неотьемлемой частью реализации класса 5! пд1етоп.