2016-08-07 4 views
1

Следующий код представляет собой упрощенную версию VisitorPattern, которую я реализовал в своем проекте.C++ Visitor Pattern and Polymorphism

#include <iostream> 
class AVisitor { 
public: 
    virtual void visit(class A *) = 0; 
}; 

class ExtendedVisitor : public AVisitor { 
public: 
    virtual void visit(class B *) = 0; 
}; 

class A { 
public: 
    virtual void accept(AVisitor *visitor) { 
     std::cout << "Call accept of A" << std::endl; 
     visitor->visit(this); 
    } 
}; 

class B : public A { 
public: 
    void accept(AVisitor *visitor) override { 
     std::cout << "Call accept of B" << std::endl; 
     B *just_this = this; 
     visitor->visit(just_this); //why this calls to visit(A*) 
     visitor->visit((B*) just_this); //useless casting 
    } 
}; 

class ActualVisitor : public ExtendedVisitor { 
public: 
    void visit(A *x) override { 
     std::cout << "Call visit on A*" << std::endl; 
    } 
    void visit(B *x) override { 
     std::cout << "Never called" << std::endl; 
    } 
}; 

int main() { 
    ActualVisitor visitor; 
    A *a = new B(); 
    a->accept(&visitor); 
} 

Я не понимаю, почему метод принимает класса B вызывает к посетителю (A *) метод вместо посетителей (B *). Основные функции принты

Call accept of B 
Call visit on A* 
Call visit on A* 

Вместо этого следующий код ведет себя, как я ожидал:

#include <iostream> 

class AVisitor { 
public: 
    virtual void visit(class A *) = 0; 
    virtual void visit(class B *) = 0; 
}; 

class A { 
public: 
    virtual void accept(AVisitor *visitor) { 
     std::cout << "Call accept of A" << std::endl; 
     visitor->visit(this); 
    } 
}; 

class B : public A { 
public: 
    void accept(AVisitor *visitor) override { 
     std::cout << "Call accept of B" << std::endl; 
     B *just_this = this; 
     visitor->visit(just_this); //now it works 
     visitor->visit((B*) just_this); 
    } 
}; 

class ActualVisitor : public AVisitor { 
public: 
    void visit(A *x) override { 
     std::cout << "Call visit on A*" << std::endl; 
    } 
    void visit(B *x) override { 
     std::cout << "Call visit on B*" << std::endl; 
    } 
}; 

int main() { 
    ActualVisitor visitor; 
    A *a = new B(); 
    a->accept(&visitor); 
} 

прямо сейчас печатает:

Call accept of B 
Call visit on B* 
Call visit on B* 

Проблема тогда, кажется, удел AVisitor класс. Интересно, почему это происходит и что такое правильный способ разработки VisitorPattern с «специализированными» посетителями (здесь ExtendedVisitor можно также посетить объект B)

ответ

1

вашего Windows B::accept имеет следующую подпись:

void accept(AVisitor *visitor) override; 

Итак, давайте проверим интерфейс AVisitor. Он имеет

virtual void visit(class A *) = 0; 

и все, что у него есть (в вашей первой версии). Это правда, что ExtendedVisitor имеет

virtual void visit(class B *) = 0; 

но не переопределить метод в AVisitor. Фактически, вторая версия поможет вам понять, почему. Поскольку

virtual void visit(class A *) = 0; 
virtual void visit(class B *) = 0; 

может находиться вместе в одном классе (они перегрузки в вашей второй версии), то они являются различными методами в этом отношении.

+0

Класс ActualVisitor переопределяет оба метода посещения (A *) и посещения (B *), поэтому, поскольку подпись accept принимается (AVisitor * visitor), полиморфизмом, я ожидаю, что вызов посетителя-> посетит (B *), выбрать правильный метод. Что мне не хватает? –

+0

Ваш код не относится к интерфейсу 'ActualVisitor', а относится к интерфейсу' AVisitor'. * Перегруженная * виртуальная функция в 'ActualVisitor' * не * переопределяет метод в' AVisitor'. –

+0

Вижу, вы правы. Тогда, если ActualVisitor не переопределяет посетителя (B *), это означает, что у меня есть не абстрактный класс, который не реализует все чистые виртуальные функции (здесь вы можете посетить (A *) и посетить (B *))? или посещение (A *) - это реализация как посещения (A *), так и посещения (B *) чистых виртуальных функций? –

0

Вы неверный пользователь. Вот правильный путь:

class AVisitor { 
public: 
    virtual void visit(class A *) = 0; 
    virtual void visit(class B *) = 0; 
    // virtual void visit(class C *) = 0; etc 
    // a separate function for every class in your hierarchy 
}; 

, а затем

class ActualVisitor : public Visitor ... 

Там нет необходимости ExtendedVisitor.

Да AVisitor должен знать о каждом классе вашей иерархии. Это главный недостаток этой картины.