【问题标题】:function override with derived argument使用派生参数覆盖函数
【发布时间】:2018-07-19 23:16:01
【问题描述】:

我想创建一个碰撞系统,其中一个基类代表场景中的一个对象,实现所有的碰撞逻辑,当检测到碰撞时,程序逻辑调用派生类函数。我面临的问题是,基类需要了解所有派生类,以便不同函数中的调度正常工作。

例如,基类,OnCollisionDetected 将被派生类覆盖以处理冲突

#include <iostream>

class BasePhysicsObject {
public:

    void Collides(BasePhysicsObject * another_object) {
        /* ... */
        bool collides = true;

        if (collides) this->OnCollisionDetected(another_object);
        return;
    }

    /* Function to be overriden */
    virtual void OnCollisionDetected(BasePhysicsObject * another_object) = 0;
};

场景中的两个虚拟类,重写了函数 OnCollisionDetected(BasePhysicsObject * another_object),根据 this 参数将调用分派给适当的函数。

class Fire;

class Player : public BasePhysicsObject {
public:

    virtual void OnCollisionDetected(BasePhysicsObject * another_object) {
        /* double dispatch to specific implementation */
        another_object->OnCollisionDetected(this);
    }

    virtual void OnCollisionDetected(Fire * fire) {
        /* Collision with fire object*/
    }
};

class Fire : public BasePhysicsObject {
public:

    virtual void OnCollisionDetected(BasePhysicsObject * another_object) {
        /* double dispatch to specific implementation */
        another_object->OnCollisionDetected(this);
    }

    virtual void OnCollisionDetected(Player * player) {
        /* Collision with player object */
    }
};

主函数创建两个对象,并检查它们的碰撞。

int main(int argc, char ** argv){

    Player * player = new Player();
    Fire * fire = new Fire();

    fire->Collides(player);
}

最终发生的事情是,从Collides() 调用的Fire::OnCollisionDetected(BasePhysicsObject * another_object) 不会调用以派生类为参数的函数,即Player::OnCollisionDetected(Fire * fire),而是再次回调函数的Player::OnCollisionDetected(BasePhysicsObject * another_object),导致在堆栈溢出中。 据我了解,为了使双重分派工作,我需要在所有派生类的基类中声明OnCollisionDetected(Derived *),但这是一个令人生畏的解决方案。还有其他方法吗?

【问题讨论】:

  • 在我看来像 visitor 模式。
  • @NeilButterworth 感谢您的回复,那么如果 then else 是多次调度的答案?
  • 要使这种方法起作用,您需要BasePhysicsObject 才能拥有virtual void OnCollisionDetected(Fire *) = 0; 和virtual void OnCollisionDetected(Player *) = 0;。事实上,当您调用another_object-&gt;OnCollisionDetected(this); 时,BasicPhysicsObject 具有的唯一 重载是OnCollisionDetected(BasePhysicsObject * another_object)。从不考虑派生类型重载。他们有virtual 成员,但不覆盖任何内容。使用override 将有助于识别问题。这个问题的一般解决方案是使用访问者。
  • 您不能使用具有一组不同参数的后代方法覆盖具有一组参数的虚方法,即使参数类型是相关的。覆盖方法的签名必须与它覆盖的方法完全匹配。只有return values are allowed to be covariant,没有参数。

标签: c++ polymorphism


【解决方案1】:

对于双重调度模式,您必须有一个虚拟调度程序。 第一次调度是通过对 lhs 实例的虚拟调用完成的,这比使用 rhs 实例的调度要好:

class BasePhysicsObject {
public:
    virtual ~BasePhysicsObject() = default;

    virtual void CollideDispatcher(BasePhysicsObject* ) = 0;

    // The true collision code.
    virtual void OnCollisionDetected(Fire*) = 0;
    virtual void OnCollisionDetected(Player*) = 0;
};

class Player : public BasePhysicsObject {
public:
    // Always same implementation
    // but `this` type is different for each class
    // Which allow correct overload resolution
    void CollideDispatcher(BasePhysicsObject* rhs) override { rhs->OnCollisionDetected(this); }

    void OnCollisionDetected(Fire* rhs) override { /* Player/Fire collision */ }
    void OnCollisionDetected(Player* rhs) override { /*Player/Player collision*/ }
};

class Fire : public BasePhysicsObject {
public:
    // Always same implementation
    // but `this` type is different for each class
    // Which allow correct overload resolution
    void CollideDispatcher(BasePhysicsObject* rhs) override { rhs->OnCollisionDetected(this); }

    void OnCollisionDetected(Fire* rhs) override { /* Fire/Fire collision */ }
    virtual void OnCollisionDetected(Player* rhs) override {
         // Fire/Player collision:
         // might be symmetrical to Player/Fire collision and so:
         rhs->OnCollisionDetected(this);
    }
};

【讨论】:

  • 谢谢,我有点希望有一些方法可以避免在基类中声明所有派生类,因为在复杂的场景中这些可能很多,你必须更改基类每次添加新的派生类...
  • 我已经完成了multiple dispath visitor,它最大限度地减少了所需的依赖关系。可能对您的需求有点过头了。
猜你喜欢
  • 2013-09-11
  • 1970-01-01
  • 1970-01-01
  • 2021-06-09
  • 2020-01-16
  • 1970-01-01
  • 2016-05-24
  • 1970-01-01
  • 2018-12-23
相关资源
最近更新 更多