【问题标题】:C++ struct implement derived interfaceC++结构实现派生接口
【发布时间】:2017-05-30 21:14:44
【问题描述】:

在 C++ 中尝试访问者模式时,我遇到了一个关于实现派生接口的愚蠢问题。我怀疑我不知道如何正确地提出这个问题,因为我在其他地方没有找到解决方案。

我有以下基本结构:

struct Visitor {
    virtual void visit(const Resources) = 0;
    virtual void visit(const Population) = 0;
};

我希望声明一些访客的具体实现以及一些额外的功能。 这就是我希望声明的样子

struct EndVisitor : public Visitor{
    virtual bool hasEnded();
};

struct SetupVisitor : public Visitor{
};

struct ScoreVisitor : public Visitor{
    virtual unsigned int getScore();
};

在定义例如 ScoreVisitor 时,IDE 和编译器会识别 ScoreVisitor 中的额外函数声明:

unsigned int ScoreVisitor::getScore() {
    return total;
}

但是,编译器或 IDE (Funtion 'visit' not declared in class 'ScoreVisitor') 无法识别实现访问者函数:

void ScoreVisitor::visit(const Resources resources) {
    total += resources.score;
}

void ScoreVisitor::visit(const Population population) {
    total += population.score;
}

如果我声明 ScoreVisitor 重复访问者函数,代码会编译,但是这会在访问者的所有专门声明中留下大量复制粘贴的代码,我希望避免这种情况。 这不是我希望声明的样子

struct ScoreVisitor : public Visitor{
    virtual void visit(const Resources);
    virtual void visit(const Population);
    virtual unsigned int getScore();
};

如何声明特定版本的访问者,而无需复制粘贴访问者已声明的所有功能?

【问题讨论】:

  • 欢迎使用 C++!在定义它们之前,您需要正确声明任何类的所有成员。甚至是您从基类覆盖的虚拟方法。
  • 谢谢尼姆!您的回复我理解为:我将无法避免在访问者的专门声明中声明接口函数?
  • 是的,除非您有一个层次结构,其中某些功能在不同的抽象级别上实现..
  • 好的,这回答了我的问题。你会用你的 cmets 提供的信息来回答这个问题吗?重要的细节是我正在尝试的事情是不可能的。

标签: c++ struct visitor-pattern


【解决方案1】:

没有办法避免必须在派生类中声明从基类重写的方法。这就是语言的方式。通常人们将功能分组到某种形式的继承层次结构中以公开通用功能。

注意一些与语法相关的问题,virtual 对于派生类是可选的(具有类似签名的函数默认是虚拟的),从 C++11 开始,有些人已经开始使用 override(我跌倒了)也属于这一类),因为它会在编译时捕捉到任何情况,即派生类中的方法应该是虚拟的,但在基类中它没有被声明为虚拟的。

我确定以上只是一个示例,但重要的是不要忘记基类中的虚拟析构函数!

【讨论】:

    【解决方案2】:

    这并不理想,但您可以使用继承自 Visitor 的子类并让其他类派生自该子类:

    struct Visitor {
        virtual void visit(const int) = 0;
    };
    
    struct VisitorImplementor : public Visitor
    {
        virtual void visit(const int) override { /* implement */}
    };
    
    
    struct EndVisitor : public VisitorImplementor {
        virtual bool hasEnded() { return true; }
    };
    

    现在您可以创建 EndVisitor 实例并对其调用访问

    EndVisitor v;
    
    v.visit(10);
    

    【讨论】:

    • 感谢发球!我看到了实现这些函数如何使它们在派生类中可见。但是,这与问题的相反方向移动:我希望尽可能地懒惰,因此我想排除派生类的实现,除了最后一个。
    猜你喜欢
    • 2017-12-26
    • 2010-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-16
    • 2010-11-06
    • 2014-06-17
    相关资源
    最近更新 更多