2

我一直在制作一个使用 Box2D 物理引擎的游戏,但我在堆栈指针 (ESP) 和多重继承方面遇到了一些奇怪的事情。我已经设法用最少的代码重现了它,而且似乎我声明要在多重继承中使用的类的顺序似乎决定了程序是否崩溃。

#include <iostream>
#include <string.h>

using namespace std;

class IPhysicsObject
{
public:
    virtual void Collide(IPhysicsObject *other, float angle, int pos)=0;
};

class IBoardFeature
{
public:
    IBoardFeature(){};
    ~IBoardFeature(){};

    virtual bool OnAttach(int x){ return true; }
    virtual bool Update(int x, float dt)=0;
};

/*
class CScorezone : public IBoardFeature, public IPhysicsObject // this breaks !!!
class CScorezone : public IPhysicsObject, public IBoardFeature // this works !!!
*/
class CScorezone : public IBoardFeature, public IPhysicsObject
{
public:
    CScorezone(){}
    ~CScorezone(void){}

    virtual bool Update(int x, float dt)
    {
        return true;
    }

    virtual void Collide(IPhysicsObject *other, float angle, int pos)
    {
    }

    virtual bool OnAttach(int x){ return true; }
};


int main(int argc, char *argv[]) 
{
    CScorezone *scoreZone = new CScorezone();
    CScorezone *otherZone = new CScorezone();

    void *voidZone = scoreZone;
    IPhysicsObject *physZone = static_cast<IPhysicsObject*>(voidZone);
    physZone->Collide(otherZone, 10, 1);

    delete scoreZone;
    delete otherZone;

    // wait for user input
    int x;
    cin >> x;
    return 0;
}

在调试模式下运行它会导致以下错误

运行时检查失败 #0 - ESP 的值未在函数调用中正确保存。这通常是调用使用一种调用约定声明的函数和使用另一种调用约定声明的函数指针的结果。

当我进入以下代码行时:

physZone->Collide(otherZone, 10, 1);

我注意到它会进入 CScoreZone::OnAttach,而不是 CScoreZone::Collide。为什么是这样?当我更改 CScoreZone 的继承顺序时,它工作正常

class CScorezone : public IPhysicsObject, public IBoardFeature

我在 Windows XP 上运行 VS2005 SP2 (8.0.50727.768)。有任何想法吗?

4

4 回答 4

4

您不必分配CScorezone*void*然后将其转换为 IPhysicsObject*. 由于CScorezone is-a IPhysicsObject您可以简单地分配给基指针:

    IPhysicsObject *scoreZone = new CScorezone();
    IPhysicsObject *otherZone = new CScorezone();

您还缺少IPhysicsObject声明中的公共虚拟析构函数。

编辑:

正如您在评论中描述的那样,我是一个回调情况(通过一些 C api?)我会使用 simplestruct和指向多态类型的指针来避免未定义的强制转换,例如:

    // one more level of indirection
    struct cb_data
    {
        IPhysicsObject* target;
    };

    // callback function
    int callback( void* data )
    {
        const cb_data& cbd( *static_cast<cb_data*>( data ));

        return cbd.target->Collide( ... );
    }
于 2010-01-24T01:45:19.113 回答
3

问题是您首先将指针转换为 void* 。编译器不知道如何为指针执行静态转换。如果使用多重继承来使用第二个超类虚拟表,则需要在强制转换期间更改指针值。只需在使用 static_cast 之前将指针转换回 CScoreZone*。

于 2010-01-24T01:22:30.340 回答
1

好吧,在您的代码中,您似乎故意通过在转换中void *用作中间类型来破坏分层转换的完整性。ScoreZone *先转换为void *,然后转换为IPhysicsObject *。结果是未定义的行为。

你为什么做这个?你期望会发生什么?

于 2010-01-24T02:02:57.397 回答
0

尼古拉用你的例子告诉你如何避免选角。但是,如果您确实需要进行类型转换,则在处理对象时始终使用 dynamic_cast,它会执行运行时类型检查。

于 2010-01-24T02:02:25.667 回答