具有派生参数的函数覆盖

时间:2018-07-19 14:45:30

标签: c++ polymorphism

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

例如,基类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 */
    }
};

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

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

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

    fire->Collides(player);
}

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

1 个答案:

答案 0 :(得分:0)

对于双重调度模式,您必须有一个虚拟调度程序。 第一次调度是通过在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);
    }
};