А. Александреску - Современное проектирование на C++ (1119444), страница 64
Текст из файла (страница 64)
Таким образом, обе опасности устраняются всего одной строчкой кода. Определение класса оосв1евепс остается прежним. Для нас представяяет интерес определение чисто виртуальной функции дссерс(оосв1евепсч1 з(согб). Затем для каждого производного класса в инспектируемой иерархии (корнем которой является класс оося1евепс) определим небольшой инспектируюший класс, имеюший только одну функцию ч(з1схкх. Например, для класса РагадгарЬ нужно определить слелуюший класс. с1азз РагадгарЬч1з1Сог ( рцЫ(с: чтгсиа1 чо1о ч1з1спагадгарЬ(РагадгарЬб) = О; )' Реализация функции Ра гад гарЬ:: асс ерс выглядит так. чо1д РагадгарЬ::десерт(ч1з1согб ч) ( И (Рагадгарйч(з1сог* р = оупав1с сазс<РагадгарЬч1з1сог*>(бч)) ( р->ч1з1срагадгарЬ( сИз); е1зе ( здесь можио вызвать функцию-ловушку необходим Нужен для инспектирования объектов класса РагадгарЬ нужен для инспектирования объектов класса аазсегв1сиар риЫ1с посв)евепсч1абсог, // риЬ11с РагадгарЬЧ(з1сог, // // рцЫ(с яазсегв1сварч1з1сог, // // ( рцЬ1(с: чоИ ч1з1срагадгарЬ(РагадгарМ сЬагз += раг.нцвсЬагзО; иогдз + раг.нцвиогдзО; чо(о чзз1саазсегв(свар(яазсегвз ++1иадез раг) сварб) Глава 10.
Шаблон Ч!вйог 265 Ведущую роль в этой функции играет оператор аупаи1с сазс, позволяюший системе поддержки выполнения программ распознать, является ли объект у подобьектом класса РагадгарЬч(з1сог, и, если да, получить указатель на этот подобъект. Аналогичные реализации функции дссерс определякпся во всех классах, производных от класса оосе1 евепс. В заключение конкретный инспектор выводится из класса оосе1- евепсчбз1со и базовых инспекторов для всех классов, представляюших интерес.
с1авв оосвсасз : Структура классов, полученная в результате, показана на рис. 10.2, Горизонтальные пунктирные линии изображают зависимости, существующие в иерархии, а вертикальная— границу между иерархиями. Обратите внимание иа то, как оператор бупавз с сает позволяет вишебиьгм образом перескакивать с одного иерархического дерева на другое, используя в качестве пружины фиктивный класс ооса1евепгИ з)тог.
Рис. 10.2. Структура классаа дан аиикаииаскага инспектора Хотя описанная структура содержит в себе большое количество деталей и взаимодействий, базовая структура довольно проста. Убедимся в этом, проанализировав поток событий. Допустим, что у нас есть указатель роося1ев на объект класса оося)евепт, имеюгцего динамический (" реальный" ) тнп рагадгарЬ. Тогда поступим следующим образом. оосвтатз зтатз; роося1ев->лссерт(зсатз); Это повлечет за собой такие события.
1. Объект зтатз автоматически преобразовывается в ссылку на объект класса оос- а1евепгМ з1тог, поскольку он является открытым наследником этого класса. 2: Вызывается виртуальная функция рагад гаро: нкссерт. Часть П. Компоненты 3. Функция яагадгарп: гдссерт пытается применить оператор бував!с сазт<яагадгарпч1з1тог*> к адресу объекта оося1евептч1з1тог„ полученного ею в качестве параметра.
Поскольку этот объект имеет динамический тип оосвтатз, который является открытым наследником классов оосе1евептч1з1тог и пагадгарпч1з1тог, выполняется приведение типов. (Вот где происходит телепортация!) 4. Теперь функция пагад гаро:: десерт должна получить указатель на часть класса пагадгар!зч1з1тог, которую унаследовал объект оосятатз. Функция пагадгарп::Десерт применяет к этому указателю виртуальную функцию Иззт.- яагадгар!з. 5. Виртуальный вызов достигает функции оосвтатз::ч1з1тпагадгар!з. Кроме того, она получает в качестве параметра ссылку на инспектируемый абзац. Инспектирование закончено. Проверим новую диаграмму зависимостей.
° Определение класса оося1евепт зависит от класса оосе1евептчтз1тог по имени. Зависимость по имени означает, что неполного объявления класса оосе1евептч1з1тог вполне достаточно. ° Класс Рагадгарйч1з1тог — и вообще все базовые классы хккМз(тог — зависят по имени от классов, которые они инспектируют.
° Реализация функции пагадгарп:гдссерт полностью зависит от класса пагадгарпч1з1тог. Полная зависимость означает, что для компиляции кода необходимо полное определение класса. ° Все конкретные определения классов инспекторов полностью зависят от класса оосвдевептч1 з1тог и всех базовых инспекторов Хххч1з1тог. Шаблон асус11с Из1тог ликвидирует циклические зависимости, но взамен оставляет программисту еще больше работы.
Теперь мы должны поддерживать деа параллельных множества классов: инспектируемую иерархию, корнем которой является класс оосе1евепт, и множество инспектируюших классов хклИз1тог, по одному на каждый инспектируемый класс. Работать с двумя параллельными иерархиями классов нежелательно, поскольку лля этого требуется строгая дисциплина и внимание.
Сравнивая эффективность "простого" шаблона Из1тог и шаблона дсус1зс Из1- тог, следует заметить, что во втором случае при каждом проходе иерархии возникает одно дополнительное динамическое приведение типов. Время, затрачиваемое на это привеление, может быть постоянным, а может зависеть от количества полиморфных классов, возрастая по логарифмическому или линейному закону. Вид этой зависимости зависит от конкретного компилятора. Если эффективность является важным критерием качества программы, то эти затраты времени могут стать сушественными.
Таким образом, иногда следует использовать "простой" шаблон чт з1тог и поддерживать циклические связи. Глядя на эту мрачную картину, следует признать, что шаблон чтвятог представляет собой противоречивую конструкцию. Это не удивительно, поскольку даже Ральф Гамма (Ва!Г Оапипа) из группы Оор заявлял, что шаблон ч1ззтог замыкает десятку самых непопулярных шаблонов (Ч((ззЫез, !999). Шаблон Иа1тог грубый, негибкий, его трудно расширять и поддерживать. Однако, будучи настойчивыми и прилежными, мы можем создать библиотечную реализа- Глава 10. Шаблон Ч!вйог 267 цию шаблона чбз1тог, представляющую собой полную противоположность сказан- ному выше: ее легко использовать, расширять и поддерживать.
Как это сделать, мы покажем в следующем разделе, Таблица 10.1. Имвнв компонвнтов Имя Принадлежность Описание вазечбабтаЫе чбз1таЫе Основа всех инспектируемых иерархий Смешанный класс, благодари которому класс в инспектируемой иерархии становится инспектируемым Основа иерархии, которую мы хотим сделать инспектируемой Даа конкретных инспектируемых класса, производных от класса посе1евепт Функция-член, которая вызывается лля инспектирования иерархии Основа инспектирующей иерархии Смешанный класс, благодаря которому класс в инспектирующей иерархии становится инспектирующим Конкретный инспектирующий класс Функция-член, которая вызывается инспектируемыми элементами н применяется к инспекторам Библиотека Библиотека ПосЕ1емепт Приложение Приложение вагаогарЬ, яазтегв1твар десерт Библиотека н приложение Библиотека Библиотека вазеч1збтог у151тог втат1зт1сз У1511 Приложение Библиотека и приложение 268 Часть П, Компоненты 10.4.
Обобщенная реализация шаблона Ч1а1тог Разделим реализацию на две основные части. ° Иислектируемме «лассы. Это классы, входящие в иерархию, которую мы собираемся инспектировать (добавляя в нее операции). ° Инспектирующие классы. Эти классы определяют и реализуют фактические операции.
Наш метод прост: мы стремимся вынести в библиотеку максимальную часть кода. Если нам это удастся, взаимозависимости между классами значительно упростятся. Таким образом, инспектор и инспектируемый будут зависеть не друг от друга, а от библиотеки. Это очень хорошо, поскольку библиотека считается намного более постоянной, чем приложение. Сначала мы попробуем реализовать обобщенный шаблон лсус11с У1з1тог, поскольку он обладает лучшими качествами с точки зрения зависимостей между его частими. Позднее мы его усовершенствуем, повысив эффективность. В заключение вернемся к реализации классического шаблона у1з1тог, прелложенного группой ОоГ, обладающего более высоким быстродействием за счет потери гибкости. При обсуждении вопросов реализации мы будем пользоваться именами, перечисленными и определенными в табл.
10.1. Некоторые из этих имен на самом деле описывают шаблонные классы, точнее говоря, становятся шаблонными классами по мере повышения степени обобщенности нашей программы. Пока нас будут интересовать лишь определения сущностей, к которым относятся эти имена. Сначала обратим внимание на инспектируюшую иерархию. Злесь все довольно просто — мы должны предусмотреть некоторые базовые классы для пользователя, т.е.
классы, определяюшие операцию ч(з1т для заданного типа. Кроме того, необходимо создать фиктивный класс, используемый оператором динамического приведения типов в соответствии с шаблоном ясус!1с ч1з(тог. с!аьа вазеИз(тог ( роЫ(с: ч(геена!»вазеч(з1согО () Кода для повторного применения здесь немного, но иногда приходится создавать и такие маленькие классы. Теперь напишем простой шаблонный класс И з1тог. севр!ате <с!азз т> с!ааа ч(з1гог риЫзс: ч1гтиа! чо1д ч(з1т(тй) = О; В обшем случае функция ч1з1сог<т>;: И з1т может возврашать значение, тип которого отличается от типа чо1д.
Зта функция может передавать полезный результат через функцию ч1з1саЫе: гдссерс. Следовательно, в класс ч1з1тог нужно добавить второй шаблонный параметр. хеяр!ате «с!авв т, турепаяе я = чо1д> с!азз ч151тог ( риЫ(с." суредет т яетцгптуре; ч1гтца! яетогптуре чзз1т(тй) = О; Для того чтобы проинспектировать иерархию,мы должны создать кпасс сопсгесеИ з(тог, производный от класса вазеИз1тог, а количество реализаций класса Из(- тог должно совпадать с количеством инспектируемых классов. с!азз ВонеИз1тог: рцЫ(с вазеИз1тог // необходим рцЫ з с И з1сог<яазте гв(тиар>, роЫ(с ч1з1тог<рагадгарЬ ( роЫ(с: чо1д ч1з1т(яазсегв(тварй) О инспектирует класс яазсегв1гвар чо(д ч(з(т(рагадгарпй) О инспектирует класс яагадгарп ); Этот код выглядит простым, ясным и легким в использовании. Его структура четко указывает, какие классы подлежат испектированию. И, что еше лучше, компилятор не позволяет конкретизировать класс вовеИз1тог, если мы не определили все функции ч1з1т.
В этом проявляется связь между определением класса вовеИз(тог и именами инспектируемых им классов (яазсегв(свар и Рагадгарп). Зта зависимость вполне понятна, поскольку класс вонеч1з1сог знает об этих типах и нуждается в специальных операциях лля работы с ними. На этом мы завершим обсуждение части реализации, касаюшейся инспектируюших классов.