[英]Casting from an abstract base class reference to derived class reference
[英]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
抽象。 Game
的GameState
實現可以直接使用它,但僅應將其用於特定於游戲的抽象。 主要的不便是:
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.