【问题标题】:How Visitor Pattern avoid downcasting访客模式如何避免向下转型
【发布时间】:2010-07-15 10:42:48
【问题描述】:

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

谢谢。

【问题讨论】:

    标签: c++ visitor-pattern


    【解决方案1】:

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

    之前

    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() 又是一个虚拟函数,根据访问者的类型进行调度(第二次调度)。

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

    【讨论】:

    • 我理解您的解释,但我想知道在示例中应用访问者模式之前的类层次结构是什么。我想知道句柄方法是在基类还是派生类中。派生类实现的典型例子是什么?
    • @peterwkc,我添加了类层次结构。它完全一样,除了没有访问者位。 Handle() 只是一个可以去任何地方(无论在哪里)的任意函数。作为层次结构一部分的派生类必须实现Accept() 并使用自身调用访问者。
    • 最后一点是国际海事组织,实际上不是不利因素,而是有利因素。通常最好在编译时强制更新访问者类,而不是跟踪数十个 if-else 语句的向下转换。
    【解决方案2】:

    wikipedia 示例使用双重调度,没有向下转换。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-08-12
      • 1970-01-01
      • 2014-07-09
      • 1970-01-01
      • 1970-01-01
      • 2018-01-18
      相关资源
      最近更新 更多