访问者:通过继承添加更多类型

Posted

技术标签:

【中文标题】访问者:通过继承添加更多类型【英文标题】:Visitor: adding more types via inheritance 【发布时间】:2011-06-13 18:34:05 【问题描述】:

我想通过继承扩展已声明的访问者,并让运行时环境搜索访问者的后代以找到要执行的正确方法。我可以在 C# 中使用它,但我希望在 C++ 中使用它。我在g ++中尝试了以下代码并且没有调用后代方法;只调用基类的方法。

#include <iostream>
using namespace std;

struct Field; // Forward declaration

struct Visitor

    virtual void visit(Field& f) = 0; // Visits Field objecs and objects derived from Field.
;

struct Field_String;

struct Visitor_Field_String : public Visitor

    // Extend the Visitor by specifying a visitation
    //     for Field_String
    virtual void visit(Field_String& fs) = 0;
;

struct Field

    void accept_visitor(Visitor& v)
    
        cout << "Field accepting visitor.\n";
        v.visit(*this);
    
;

struct Field_String : public Field

    void accept_visitor(Visitor& v)
    
        cout << "Field_String accepting visitor.\n";
        v.visit(*this);  // Line 1
    
;

struct Full_Visitor : Visitor_Field_String

    void visit(Field& f)
    
        cout << "Visiting a Field object\n";
        return;
    
    void visit(Field_String& fs)
    
        cout << " Visiting a Field_String object\n";
        return;
    
;


int main(void)

    Field_String fs;
    Full_Visitor visitor;
    fs.accept_visitor(visitor);
    return 0;

我得到以下输出:

# g++ --version
g++ (GCC) 3.4.4 (cygming special, gdc 0.12, using dmd 0.125)
Copyright (C) 2004 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# g++ -o virtual_visitor.exe virtual_visitor.cpp
# ./virtual_visitor.exe
Field_String accepting visitor.
Visiting a Field object

我想要的输出是:

Field_String accepting visitor.
Visiting a Field_String object

我的两个问题:

    为什么visit方法在 后代访客没有被处决? 如何执行visit 方法 在后代访问者中使用 多态性?

注意:目标是减少在访问者类中指定的类,方法是使用继承并允许可能不使用访问者中指定的所有类的情况。

注意:这不是双重分派,而是扩展分派。

【问题讨论】:

我认为您不了解访问者模式 【参考方案1】:

你不能在 C++ 中做到这一点(你真的可以在没有反射的情况下在 C# 中做到这一点吗?)关于特定问题:

    编译器根据引用的静态类型解析要使用的函数重载,并根据对象的动态类型解析该函数​​的最终覆盖。

    您需要在基类中提供所有不同的重载。如果你做不到,你可以做一些讨厌的事情,比如dynamic_cast 来尝试确定收到的Visitor 是否支持该特定领域,但我会不惜一切代价避免它。 调度*。

由于不同的字段类型没有被多态使用(或者至少看起来不像,因为accept_visitor 函数不是虚拟的),你为什么不接受具体的访问者类型?

struct Field_String : Field

    void accept_visitor(Visitor_Field_String& v)
    
        cout << "Field_String accepting visitor.\n";
        v.visit(*this);
    
;

【讨论】:

@dantuch:问题中的整个代码都违反了模式。虽然这两个词是visitoraccept,但事实是,在模式中,accept 方法是多态的,visitor(层次结构中的所有访问者)知道 element 层次结构中的类型。该模式是 double dispatch 的一个版本,但问题明确指出他不想要 double dispatch... 如果您再查看不同的库,您可能会发现不同的模式。在 boost 变体中,访问者是 静态访问者,accept 方法是模板。 ... 函数模板只不过是一组接受每种类型的不同函数。除此之外,visitor 不知道Field_String 类的事实还有一个额外的隐含要求,即只有Visitor_Field_String 访问者可以访问 Field_String,上面的代码是仅在代码中说明该要求。【参考方案2】:

您是否有理由必须从您的Visitor 虚拟类派生第二个Visitor_Field_String 虚拟类类型?例如,如果您像这样定义 VisitorFull_Visitor 基类:

struct Visitor

    virtual void visit(Field& f) = 0;
    virtual void visit(Field_String& fs) = 0;
;

struct Full_Visitor : public Visitor

    void visit(Field& f)
    
        cout << "Visiting a Field object\n";
        return;
    
    void visit(Field_String& fs)
    
        cout << " Visiting a Field_String object\n";
        return;
    
;

您将获得所需的功能。您尝试做的问题是fs.accept_visitor(visitor) 被多态转换为的类类型是Visitor 对象类型,它只定义了virtual void visit(Field&amp; f)。因此,在 Visitor 类型的类上调用 visit(),而无需在 accept_visitor() 函数中对 Visitor_Field_String 类类型进行某种类型的额外转换,将不会引用 visit() 函数的额外重载版本,因为它们'在派生类中定义。

【讨论】:

继承的目的是在不改变基类代码的情况下增加更多visit方法。另一个原因是基类中有通用的visit 方法,因此后代不必实现不会被调用的visit 方法。 如果您无法修改基本虚拟类,那么您将不得不想办法将适当的 dynamic_cast 语句添加到您的 accept_visitor 函数调用中,以便其中一个派生的虚拟类被传递给您的函数,而不是基类 Visitor

以上是关于访问者:通过继承添加更多类型的主要内容,如果未能解决你的问题,请参考以下文章

由于私有继承而无法访问的类型

java继承实现的基本原理

C++:类继承|| 类继承的定义访问控制protected类继承的构造函数

继承和方法重载

重载和重写

面向对象的过程继承封装多态;抽象类访问修饰符的使用引用类型强制转换方法重写@override与重载空指针异常super关键字