簡體   English   中英

從抽象基類指針到派生類的轉換

[英]Casting from abstract base class pointer to derived class

我正在嘗試為我的游戲創建一個狀態管理器,我有4個類:

游戲狀態:

  // foward declaration to avoid circular-referency
  class StateManager;

  class GameState
  {
    public:
      virtual ~GameState() { }    
      virtual void update(StateManager* gameManager) = 0;
      virtual void draw(StateManager* gameManager) = 0;

   protected:
      GameState() { }
  };

StateManager:

  class StateManager
  {
    public:
      StateManager();
      virtual ~StateManager();

      void addState(GameState* gameState);
      void update(StateManager* stateManager);
      void draw(StateManager* stateManager);

    protected:
      // store states in a unique_ptr to avoid memory leak
      std::vector<std::unique_ptr<GameState> > states_;
  };

游戲:

class Game : public StateManager
{
public:
  void compute()
  {
    // call methos of statemanager
    update(this);
    draw(this);
  }
}

和MainMenu:

class MainMenu : public GameState
{
  public:
    // override the pure virtual methos of GameState
    void update(StateManager* stateManager)
    {
      // problem here.
      // I need to handle instance of Game in this class, 
      // but the pointer is one StateManager
    }
    void draw(StateManager* stateManager) {}
}

當我這樣初始化游戲時: game.addState(new MainMenu())

我可以在MainMenu訪問類Game的唯一方法是通過投射指針?

// MainMenu class
void update(StateManager* stateManager)
{
    Game* game = (Game*) stateManager;
    game.input.getKey(ANY_KEY);
    //...
}

這是正確的嗎? 某事告訴我我做錯了。

immibis的答案非常適合解決技術鑄造問題。

但是,您的設計中有些奇怪。 因此,我想提供一個替代答案,以解決設計問題。

首先, StateManager本身不是GameState 因此,對於update()draw()不需要具有相同的簽名。 您是否曾預見過要在參數中用另一個StateManager調用這些StateManager函數之一? 對於Game ,我認為這沒有任何意義。 因此,我建議重構類(並相應地修改Game ):

class StateManager {
public:
    ...
    void update();  // they always know "this".  
    void draw();
protected:
    ...
};

接下來,似乎StateManager擁有GameState (使用unique_ptr<>的受保護矢量,並且您的強制轉換問題建議了它)。 因此,另一種設計可能也很有趣:

class GameState {
public:
    virtual ~GameState() { }    
    virtual void update() = 0;
    virtual void draw() = 0;
protected:
    GameState(StateManager* gameManager) { }  // GameStates are created for a StateManager
    StateManager* gm;   // this manager can then be used for any GameState functions that need it
};

按照此邏輯, MainMenu將重構為:

class MainMenu : public GameState
{
public:
    MainMenu (Game* g) : game(g), GameState(g) {}
    void update()
    {
        // NO LONGER NEEDED:  Game* game = (Game*) stateManager;
        game->input.getKey(ANY_KEY);
        // NO MORE PROBLEMS HERE:  you always refer to the right object without any overhead
    }
    void draw() {}
protected:  
    Game *game;   // the owning game.  
};  

這種替代設計的優點是:

  • 因為您不必總是擔心成員函數的參數,所以代碼將更加輕巧,並且不易出錯。
  • StateManager指針僅用於StateManager抽象。
  • 依賴於GameGameState實現可以直接使用它,但僅應將其用於特定於游戲的抽象。

主要的不便是:

  • 必須始終為一個特定的StateManager創建GameState
  • 設計的健壯性是以所有GameState實現(例如MainMenu中的冗余指針為代價的。 如果您有數百萬個,則可能會成為內存問題。

如果不確定stateManager是否指向Game ,請使用:

Game *game = dynamic_cast<Game*>(stateManager);

如果stateManager沒有指向Game ,則game將包含一個空指針,否則它將包含一個指向游戲的指針。

如果您確信總是一個Game ,想跳過檢查(一個微小的性能增益),使用方法:

Game *game = static_cast<Game*>(stateManager);

如果stateManager不指向Game ,它將產生不確定的行為。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM