Программирование >>  Многопоточная библиотека с принципом минимализма 

1 ... 83 84 85 [ 86 ] 87 88 89 ... 106


Эта маленькая программа выводит на экран сообщение, означающее все в порядке . 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.



1 ... 83 84 85 [ 86 ] 87 88 89 ... 106

© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки.
Яндекс.Метрика