Анимация
JavaScript
|
Главная Библионтека Эта маленькая программа выводит на экран сообщение, означающее "все в порядке". vistt(Paragraph*) Разумеется, этот искусственный пример не может продемонстрировать всю мощь разработанного нами кода. Однако, если вспомнить, с какими трудностями мы столкнулись в предыдущем разделе при реализации инспекторов "с нуля", становится ясно, что теперь в нащем распоряжении есть средство для корректного создания инспектируемых иерархий и их дальнейшего инспектирования. Перечислим действия, которые необходимо выполнить при определении инспектируемой иерархии. • Вывести корень иерархии из класса Basevisitable<YourReturnType>. • Добавить в каждый класс SomeClass, входящий в инспектируемую иерархию, макрос DEFlNE visiTABLE(). (Теперь иерархию можно инспектировать, однако никаких зависимостей от класса visitor больше нет!) • Вывести каждый конкретный инспектирующий класс из класса Basevisitor. Кроме того, для каждого класса X, подлежащего инспектированию, нужно вывести класс Somevisitor из класса visitor<x, YourReturnType>. Обеспечить замещение функции-члена Visit в каждом инспектируемом классе. Диаграмма зависимостей, возникающая в результате этих действий, очень проста. Определение класса Somevisitor зависит по имени от каждого инспектируемого класса. Реализации функции-члена visit полностью зависят от классов, которыми они манипулируют. Все это прекрасно. По сравнению с реализациями, обсуждавшимися ранее, лучшего и быть не может. Благодаря реализации шаблона Visitor у нас есть упорядоченный способ создания инспектируемых иерархий, позволяющий сократить клиентский код и зависимость классов. В особых случаях функцию Accept лучше реализовывать непосредственно, а не с помошью макроса DEFINE VISITABLE(). Допустим, что мы определяем класс Section, производный от класса DocElement и содержащий несколько объектов класса Paragraph. Мы бы хотели инспектировать все объекты класса Paragraph, содержащиеся в объекте класса Section. В этом случае мы могли бы реализовать функцию Accept вручную. class Section : public DocElement функция Accept реализуется непосредственно, а не через макрос DEFINE VISITABLE() virtual ReturnType Accept(Basevisitor* v) { for (каждый параграф в данном разделе) { current paragraph->Accept(v); Очевидно, что с помощью шаблона visitor можно делать все, что угодно. Код, приведенный выше, освобождает программиста от тяжелой необходимости создавать все с нуля. Мы закончили разработку ядра реализации шаблона visitor, содержащего практически все, что необходимо для инспектирования. Продолжение следует. 10.5. Назад - к "простому" шаблону Visitor Реализация обобщенного щаблона Acyclic visitor, определенная в предыдущем разделе, вполне удовлетворительно работает во многих ситуациях. Однако, если нужно создать быстродействующее приложение, динамическое приведение типов, выполняемое в функции Accept, может повергнуть вас в уныние, а измерения скорости работы ващей профаммы - прямо в состояние депрессии. Почему это происходит? Когда вы применяете оператор dynami c cast к некоторому объекту, система поддержки выполнения профамм должна произвести несколько действий. Код механизма RTTI должен определить, допустимо ли данное преобразование, и в случае положительного ответа вычислить указатель на объект результирующего типа. Попробуем разобраться, как этого можно достичь при создании компилятора. Можно присвоить каждому типу, который фигурирует в профамме, уникальный целочисленный идентификатор. Этот идентификатор оказывается полезен и при обработке исключительных ситуаций. Тогда в виртуальную таблицу каждого класса компилятор записывает указатель на таблицу идентификаторов всех его подтипов. Вместе с ними компилятор должен хранить смещения подобъектов относительно базового объекта. Этой информации достаточно для правильного выполнения динамического приведения типов. Когда выполняется оператор dynamic cast<T2*>(pl), а парамеф р1 представляет собой "указатель на объект типа т1", система поддержки выполнения профамм про-смафивает таблицу типов в поисках типа, соответствующего типу Т2. Если соответствие с типом т2 обнаружено, система поддержки выполнения профамм выполнит необходимые арифметические операции с указателем и вернет результат. В противном случае будет возвращен нулевой указатель. Детали, в частности множественное наследование, еще больще усложняют и замедляют динамическое приведение типов. Сложность описанного выще рещения равна 0(л), где п - количество базовых классов данного класса. Иными словами, время, зафачиваемое на динамическое приведение типов, возрастает линейно по мере углубления и расширения иерархий наследования. В качестве альтернативы можно использовать таблицы хэширования, позволяющие повысить бысфодействие программы за счет больших зафат памяти. Кроме того, можно применить большую мафицу для всего приложения. В этом случае время, затрачиваемое на динамическое приведение типов, постоянно, однако размер профаммы резко возрастает, особенно, если в ней много классов. (Можно было бы сжать матрицу - тогда жизнь создателей компиляторов языка С-Ы- стала бы намного легче.) Итак, оператор dynami c cast значительно снижает производительность профаммы, причем предсказать величину этого снижения невозможно. В некоторых случаях это оказывается совершенно неприемлемым. Следовательно, мы должны расширить нашу реализацию шаблона visitor, чтобы адаптировать "циклический" шаблон visitor, предложенный фуппой GoF. Это позволит повысить бысфодействие нашего приложения, так как в "циклическом" шаблоне visitor не используется динамическое приведение типов, хотя поддерживать его фуднее. Работа шаблона visitor, предложенного группой GoF, уже описывалась в начале главы. Ниже перечисляются основные различия между "обычным" шаблоном visitor и шаблоном Acyclic visitor, реализованным в библиотеке Loki. • Класс Basevisitor больше не является фиктивным. В нем определяется одна чисто виртуальная функция-член visit для каждого инспектируемого типа (в предположении, что мы используем перефузку функций). • функция Acceptimpl должна измениться. В идеале макрос DEFINE VISITABLE() остается неизменным. Все это сводится к следующему. У нас есть набор типов, подлежащих инспектированию: например, DocElement, Paragraph и RasterBitmap. Как выразить этот набор типов и манипулировать с ним? Естественно в этот момент на ум приходят списки типов, описанные в главе 3. Списки типов - это именно то, что нам нужно. Мы хотим передавать список типов шаблонному классу CyclicVisitor в качестве шаблонного параметра, как бы говоря: "Я хочу, чтобы данный класс CyclicVisitor мог инспектировать данные типы". Сделать это можно следующим элегантным способом. неполное объявление, необходимое для списка типов class DocElement; class Paragraph; class RasterBitmap; инспектирует классы DocElement, Paragraph и RasterBitmap typedef CyclicVisitor < void, тип возвращаемого значения TYPELIST 3(DocElement, Paragraph, RasterBitMap) > Myvisitor; Класс CyclicVisitor зависит no имени от классов DocElement, Paragraph и RasterBitmap. Посмотрим, какие дополнения нужно сделать в нашем коде. Используем процедуру, описанную в главе 9 при определении обобщенной реализации шаблона Abstract Factory. Определение класса Private::visitorBinder<R> содержится в файле visitor.h template <typename R, class TList> class CyclicVisitor : public GenmScatteredHierarchy<TList, Private::visitorBinder<R>::Result> typedef R ReturnType; template <class visited> ReturnType visit(visited& host) { visitor<visited>& subobj = *this; return subobj.visit(host); Следует отметить, что класс CyclicVisitor использует класс visitor в качестве строительного блока. Аналогичный способ был продемонстрирован в главе 3, а похожий пример был рассмотрен в главе 9 По существу, класс CyclicVisitor наследует класс visitor<T> для каждого типа т, указанного в списке TList. Если передать классу CyclicVisitor список типов, он завершит наследование от класса visitor, конкретизированного для каждого типа, указанного в данном списке, объявив таким образом по одной чисто виртуальной функции visit для каждого типа. Иными словами, с функциональной точки зрения он эквивалентен базовому классу visitor, как это предписывает щаблон visitor, предложенный группой GoF. 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 |