Анимация
JavaScript


Главная  Библионтека 

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

оболочка оператора dynamic cast, и шаблонный класс visitor, генерируюший чисто виртуальную функцию vi si t.

Несмотря на то что код, написанный нами для этого, весьма мал, его потенциал повторного использования офомен. Шаблонный класс visitor генерирует отдельный тип для каждого инспектируемого класса. Пользователь библиотеки теперь может не разбивать реализацию на такие маленькие классы, как ParagraphVisitor и Raster-visitor. Типы» генерируемые шаблонным классом visitor, сами обеспечат связь между всеми инспектируемыми классами.

Перейдем к инспектируемой иерархии. Как указывалось в предьщущем разделе, инспектируемая иерархия в реализации шаблона Acyclic visitor предназначена для решения следующих задач.

• Объявлять чисто виртуальную функцию Accept, получающую ссылку на объект класса visitor в базовом классе.

• Замещать и создавать реализацию функции Accept в каждом производном классе.

Для того чтобы выполнить первую задачу, мы добавим в класс Basevi si table чисто виртуальную функцию и пофебуем, чтобы класс DocElement наследовал ее. Это относится и ко всем корням инспектируемых иерархий, существующих в приложении. Класс Basevi si table выглядит следующим образом.

template <typename R = void>

class Basevisi table

public:

typedef R ReturnType;

virtual -BasevisitableC) {}

virtual ReturnType AcceptCBasevisitorA) = 0;

Bee очень просто и скучно. Интересные вещи происходят, лишь когда мы пытаемся выполнить вторую задачу, т.е. реализовать функцию Accept в библиотеке (а не в клиентском коде). Реализация функции Accept невелика, но включать ее в каждый клиентский класс - слишком утомительное занятие. Было бы здорово, если бы этот код принадлежал библиотеке. Как предписьгеает шаблон Acyclic visitor, если класс т является инспектируемым, то реализация т:: Accept применяет оператор dynami c cast<vi si tor<T>*> к типу Basevisitor. Если приведение вьшолнено успешно, функция т::Accept возвращает управление функции visitor<T>::visit. Однако, как указьгеалось в разделе 10.2, простое определение функтши Accept в классе Basevi si table не работает, поскольку парамеф *this имеет статический тип Basevi si table, который не может предоставить инспекторам достаточный объем информащти о типе.

Нужно найти способ реализовать функцию Accept в библиотеке, а затем вставить ее в иерархию класса DocEl ement. Увы, в языке С-Ы- такого непосредственного механизма нет. Есть средства для работы с виртуальным наследованием, однако они работают не лучхшш образом. Мы должны прибегнуть к макросу и потребовать, чтобы каждый класс в инспектируемой иерархии использовал этот макрос в своем определении.

Принять решение использовать макрос (со всеми вытекающими отсюда последствиями) нелегко, но другого более удобного и эффективного решения не существует. Поскольку профаммисты на языке С-Ы- люди практичные, эффективность является достаточной причиной для использования именно макроса, а не другого, эзотерического, но неэффективного средства.



Основное правило гласит: макрос должен выполнять как можно меньше работы и как можно быстрее пересылать данные "реальным" сущностям (функции или классу). Макрос для инспектируемых классов определяется следующим образом.

#define define visitableC) \

virtual ReturnType AcceptCBasevisitor& guest) \ { return Acceptimpl(*this, guest); }

Пользователь должен вставить макрос define VISITABLEC) в каждый класс инспектируемой иерархии. Этот макрос переопределяет функцию-член Accept как шаблонную функцию Acceptimpl, параметризованную типом параметра *this. Таким образом, функция Acceptimpl получает доступ к необходимому статическому типу. Функция Acceptimpl определена в самом низу иерархии, в классе Basevisitor. Это позволяет всем производным классам иметь к ней доступ. Вот как выглядит измененный класс Basevisi table.

template <typename R = void>

class Basevisi table

public:

typedef R ReturnType;

virtual -Basevisi tableC) {}

virtual ReturnType Accept(Basevisitor&) = 0; protected: Открывает доступ к иерархии

template <class т>

static ReturnType Acceptimpl(т& visited, Basevisitor* guest) {

применяем шаблон Acyclic visitor

if Cvisitor<T>* p = dynamic cast<visitor<T>*>C&guest))

return p->visitCvisited);

return ReturnTypeC);

To, что мы отправили функцию Acceptimpl в библиотеку, очень важно. Это сделано не только для автоматизации работы пользователя. Присутствие функции Acceptimpl в библиотеке дает нам возможность настраивать ее реализацию на конкретные условия проекта.

Реализация проекта visitor/visitable скрывает от пользователя большое количество деталей, представляя собой волшебный черный ящик. Ниже приведен код реализации обобщенного шаблона Acyclic visitor.

инспектирующая часть

class Basevisitor

public:

virtual -BasevisitorC) {}

template <class T, typename R = void>

class visitor

public:

typedef R ReturnType; Доступен для клиентов virtual ReturnType visit(T&) = 0;



инспектируемая часть template <typename R = void> class Basevisi table {

public:

typedef R ReturnType;

virtual ~Basevisitable() {}

virtual ReturnType Accept(Basevisitor*) = 0; protected:

template <class т>

static ReturnType Acceptimpl(T& visited, Basevisitor* guest) {

применяем шаблон Acyclic Visitor

if (visitor<T>* p = dynamic cast<visitor<T>*>(*guest))

return p->visit(visited);

return ReturnTypeO;

#define DEFINE VISITABLEC) \

virtual ReturnType Accept(Basevisitor* guest) \ { return Acceptimpl(*this, guest); }

Готовы к тестовым испытаниям? Начнем!

class DocElement : public Basevi si tableo {

public:

DEFINE VISITABLE()

class Paragraph : public DocElement {

public:

DEFINE VISITABLE()

class MyConcretevisitor :

public Basevisitor, необходим

public visitor<DocElement>, инспектирует объекты

класса DocElements public visitor<Paragraph> инспектирует объекты

класса Paragraph

public:

void visit(DocElement*)

{ std::cout « "visit(DocElement*) \n"; } void visit(Paragraph&)

{ std::cout « "visit(Paragraph*) \n"; }

int mainO {

MyConcretevisitor visitor; Paragraph par;

DocElement* d = &par; Скрывает статический тип объекта par d->Accept(visitor);



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