代码之家  ›  专栏  ›  技术社区  ›  XwipeoutX

在C++中使用多重继承时无效ESP(VS2005)

  •  2
  • XwipeoutX  · 技术社区  · 16 年前

    我一直在做一个游戏,使用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
    

    我在WindowsXP上的VS2005SP2(8.0.50727.768)上运行。有什么想法吗?

    4 回复  |  直到 9 年前
        1
  •  3
  •   shura    16 年前

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

        2
  •  4
  •   Nikolai Fetissov    16 年前

    你不必指定 CScorezone* 到 void* 然后投给 IPhysicsObject* . 自从 CScorezone IS-A IPhysicsObject 您可以简单地分配给基指针:

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

    你还丢失了 物理对象 宣言。

    编辑:

    我是你在评论中描述的回调情况(通过一些C API?)我会用简单的 struct 使用指向多态类型的指针来避免未定义的强制类型转换,例如:

        // 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( ... );
        }
    
        3
  •  1
  •   AnT stands with Russia    16 年前

    好吧,在代码中,您似乎故意使用 void * 作为演员的中间类型。 ScoreZone * 被铸造成 空洞* 先投后投 IPhysicsObject * . 你得到的结果是未定义的行为。

    你为什么这么做?你以为会发生什么?

        4
  •  0
  •   Ozan HELPY    16 年前

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