C++ 访问者模式和多态性

C++ Visitor Pattern and Polymorphism

以下代码是我在我的项目中实现的 VisitorPattern 的简化版本。

#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);
}

我不明白为什么 class B 的 accept 方法调用的是 visitor(A*) 方法而不是 visitor(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 的继承上 class。我想知道为什么会发生这种情况以及设计具有 "specialized" 访问者的 VisitorPattern 的正确方法是什么(这里 ExtendedVisitor 也可以访问 B 对象)

您的 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;

可以一起驻留在同一个 class 中(它们在您的第二个版本中是重载),那么在这方面它们是不同的方法。

您未正确实施访客。正确的方法是:

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 必须了解您的层次结构中的每个 class。这是此模式的主要缺点。