首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在基类中调用虚拟方法时,是否可以将继承重构为组合?

在基类中调用虚拟方法时,是否可以将继承重构为组合?
EN

Software Engineering用户
提问于 2011-06-06 17:41:35
回答 2查看 1.9K关注 0票数 4

假设我有一个名为Country的类和两个名为AI和Player的子类。国家级有许多虚拟方法来允许特定玩家或特定于AI的行为.

我想让玩家在一场比赛中转换国家成为可能。为此,我正在重构与组合的继承关系。然后,我将能够切换一个球员的国家的内部实例在任何时候的另一个。

但这带来了一个问题:当外部对象试图调用播放机上的这些虚拟方法之一时,在完成任何适当的处理之后,将正确地委托给Country方法。但是,当内部Country方法调用其中一个虚拟方法时,播放器或AI版本中的代码就不再有机会运行了。

因此,现在我需要更新这些方法中的每一个,以确保两个版本都被调用,这本身并不是一个简单的任务(无限循环,有人吗?)

这似乎比该做的要困难得多。我以前做过类似的重构,从来没有遇到过这个问题。为什么?我是不是漏掉了一些显而易见的观察结果?

既然我再次访问了代码,请编辑一个具体的示例:

代码语言:javascript
复制
class Country
{
public:
    virtual void AddMoney(float money)
    {
        _vMoney += money;
    }
    void TakeLoan()
    {
        float loan = ...;
        AddMoney(loan); // <-- does not route through AI or Player implementation
    }
}
代码语言:javascript
复制
class AI  // : public Country
{
private:
    Country *pCountry;

public:
    virtual void AddMoney(float x)
    {
        pCountry->AddMoney(x); // formerly Country::AddMoney(x)
        AllocateMoney(x);
    }
}
EN

回答 2

Software Engineering用户

发布于 2011-06-06 17:52:02

所以让我们把这个做成混凝土吧。

代码语言:javascript
复制
class Country {
  virtual void foo();
  void bar() {
    foo();
  }
}

向以下方向移动:

代码语言:javascript
复制
class Country {
  private thingWhichWasSubclass;
  void bar() {
    thingWhichWasSubclass.foo();
  }
}

是那么回事吗?作为中间的一步怎么样:

代码语言:javascript
复制
class Country {
  private thingWhichWasSubclass;
  void foo() {
    thingWhichWasSubclass.foo();
  }
  void bar() {
    foo();
  }
}

这能行吗?然后(当然)内联Country.foo()。

票数 5
EN

Software Engineering用户

发布于 2015-05-08 09:09:46

我也有同样的问题,但这次是用Java。我找到了一种在java中使用非静态嵌套类的解决方案--在C++中它可以转换成如下所示:

代码语言:javascript
复制
class AI  // : public Country
{
private:
    Country *pCountry; 

public:
    class Super : public Country {
        AI* outerThis;

    public:
        Super(AI* outer, ...)
        {
            outerThis = outer;
        }


        virtual void AddMoney(float x)
        {
            Country::AddMoney(x);
            outerThis->AllocateMoney(x);
        }
    } 

    // Just delegating calls to pCountry...
    virtual void AddMoney(float x)
    {
        pCountry->AddMoney(x);
    }
}

当然,您需要实例化AI::Super以便获得与特定AI对象相关联的国家。也许可以从AI对象本身实例化它(这就消除了AI:Super的需要)。

基本上,用构图代替继承就是在场景后面做些什么。在场景后面,它的组合加上对虚拟函数调度表的扩展。在没有虚拟函数的情况下,这是直接的,但在虚拟函数的情况下,您将不得不考虑这些因素。通常这样做的方法(幕后)是让由基类的条目(用overriden函数替换)组成的虚拟表,然后是派生类的条目,但它也可以通过为基对象(重写的函数替换)和派生类中的新的虚拟方法的vtable(如我的例子)实现。在后一种情况下,可以单独分配基本对象,并只存储指向它的指针。

唯一的循环引用她是AI <-> AI::Super,这可能导致循环调用。然而,从AI到AI::Super的唯一调用应该是包装来自基类的方法,而来自AI::Super的调用类似于派生类中的调用方法,不应该出现无限递归。

票数 1
EN
页面原文内容由Software Engineering提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://softwareengineering.stackexchange.com/questions/81976

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档