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::CollideではなくCScoreZone::OnAttachに入っていることに気づきました。どうしてこれなの?CScoreZoneの継承の順序を変更すると、正常に機能します

class CScorezone : public IPhysicsObject, public IBoardFeature

WindowsXPのVS2005SP2(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を通過しますか?)私は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( ... );
    }
于 2010-01-24T01:45:19.113 に答える
3

問題は、最初にvoid*へのポインタをキャストすることです。コンパイラは、ポインタの静的キャストを実行する方法を知りません。多重継承を使用して2番目のスーパークラス仮想テーブルを使用する場合は、キャスト中にポインター値を変更する必要があります。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 に答える