Как Шаблон "посетитель" избегает downcasting

кто-либо может показать пример кода прежде и после постараться не вниз бросать для кода шаблона "посетитель"?

Спасибо.

11
задан nicholas 15 July 2010 в 10:42
поделиться

2 ответа

Голый, минималистичный пример.

До

class Base {};
class Derived1 : public Base {};
class Derived2 : public Base {};

// Some arbitrary function that handles Base.
void
Handle(Base& obj) {
    if (...type is Derived1...) {
        Derived1& d1 = static_cast<Derived1&>(base);
        std::printf("Handling Derived1\n");
    }
    else if (...type is Derived2...) {
        Derived2& d2 = static_cast<Derived2&>(base);
        std::printf("Handling Derived2\n");
    }
}

Это означает, что Base должен иметь некоторое поле тега type, или вы будете использовать dynamic_cast для проверки каждого типа.

После

// Class definitions
class Visitor;
class Base {
public:
    // This is for dispatching on Base's concrete type.
    virtual void Accept(Visitor& v) = 0;
};
class Derived1 : public Base {
public:
    // Any derived class that wants to participate in double dispatch
    // with visitor needs to override this function.
    virtual void Accept(Visitor& v);
};
class Derived2 : public Base {
public:
    virtual void Accept(Visitor& v);
};
class Visitor {
public:
    // These are for dispatching on visitor's type.
    virtual void Visit(Derived1& d1) = 0;
    virtual void Visit(Derived2& d2) = 0;
};

// Implementation.
void
Derived1::Accept(Visitor& v) {
    v.Visit(*this); // Calls Derived1 overload on visitor
}
void
Derived2::Accept(Visitor& v) {
    v.Visit(*this); // Calls Derived2 overload on visitor
}

Это был фреймворк. Теперь вы реализуете фактический посетитель для полиморфной обработки объекта.

// Implementing custom visitor
class Printer : public Visitor {
    virtual void Visit(Derived1& d1) { std::printf("Handling Derived1\n"); }
    virtual void Visit(Derived2& d2) { std::printf("Handling Derived2\n"); }
};

// Some arbitrary function that handles Base.
void
Handle(Base& obj)
{
    Printer p;
    obj.Accept(p);
}
  1. Accept() - это виртуальная функция, которая диспетчеризирует тип obj (первая диспетчеризация)
  2. Затем она вызывает соответствующую перегрузку Visit(), потому что внутри Accept() вы уже знаете тип вашего объекта.
  3. Visit(), в свою очередь, является виртуальной функцией, которая диспетчеризирует по типу посетителя (вторая диспетчеризация).

Поскольку у вас двойная диспетчеризация (одна на объект, другая на посетителя), вы не делаете никакого приведения. Недостатком является то, что каждый раз, когда вы добавляете класс в иерархию, вы должны пойти и обновить свой класс посетителя, чтобы добавить соответствующую функцию для обработки нового подкласса.

21
ответ дан 3 December 2019 в 04:12
поделиться

Пример wikipedia использует двойную отправку, без понижающего преобразования.

4
ответ дан 3 December 2019 в 04:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: