11

任何人都可以在之前和之后显示示例代码以避免访问者模式代码的向下转换吗?

谢谢。

4

2 回答 2

23

一个简单的、简约的例子。

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必须有一些类型标签字段,否则您将使用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()反过来,是一个按访问者类型分派的虚函数(第二次分派)。

因为您有双重调度(一个在对象上,另一个在访问者上),所以您不进行任何转换。不利的一面是,每当您将一个类添加到您的层次结构中时,您都必须去更新您的访问者类以添加一个适当的函数来处理新的子类。

于 2010-07-15T11:07:53.907 回答
4

维基百科示例使用双重调度,没有向下转换。

于 2010-07-15T11:00:59.303 回答