[英]Casting from abstract base class pointer to derived class
I'm trying to create a state manager for my game, and I have 4 classes: 我正在尝试为我的游戏创建一个状态管理器,我有4个类:
GameState: 游戏状态:
// 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: 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_;
};
Game: 游戏:
class Game : public StateManager
{
public:
void compute()
{
// call methos of statemanager
update(this);
draw(this);
}
}
And MainMenu: 和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) {}
}
When I initialize my game like so: game.addState(new MainMenu())
. 当我这样初始化游戏时:
game.addState(new MainMenu())
。
The only way I can access the class Game
in MainMenu
is by casting the pointer? 我可以在
MainMenu
访问类Game
的唯一方法是通过投射指针?
// MainMenu class
void update(StateManager* stateManager)
{
Game* game = (Game*) stateManager;
game.input.getKey(ANY_KEY);
//...
}
Is this right? 这是正确的吗? Something tells me I'm doing it wrong.
某事告诉我我做错了。
The answer of immibis is perfect for addressing the technical casting issue. immibis的答案非常适合解决技术铸造问题。
Nevertheless there's something weird in your design. 但是,您的设计中有些奇怪。 So I'd like to provide an alternative answer, addressing the design issues.
因此,我想提供一个替代答案,以解决设计问题。
First a StateManager
is not itself a GameState
. 首先,
StateManager
本身不是GameState
。 So there is no need to have the same signature for update()
and draw()
. 因此,对于
update()
和draw()
不需要具有相同的签名。 Do you ever foresee to have one of these StateManager
functions called with another StateManager
in argument ? 您是否曾预见过要在参数中用另一个
StateManager
调用这些StateManager
函数之一? For a Game
, I think this makes no sense. 对于
Game
,我认为这没有任何意义。 So I'd recommend to refactor the class (and adapt Game
accordingly): 因此,我建议重构类(并相应地修改
Game
):
class StateManager {
public:
...
void update(); // they always know "this".
void draw();
protected:
...
};
Next, it seems that the StateManager
owns the GameState
(the protected vector using a unique_ptr<>
,and your casting question suggest it). 接下来,似乎
StateManager
拥有GameState
(使用unique_ptr<>
的受保护矢量,并且您的强制转换问题建议了它)。 So another design could be interesting as well: 因此,另一种设计可能也很有趣:
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
};
Following this logic, the MainMenu
would be refactored as: 按照此逻辑,
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.
};
The advantages of this alternative design are: 这种替代设计的优点是:
StateManager
pointer is used only for the StateManager
abstraction. StateManager
指针仅用于StateManager
抽象。 GameState
, which rely on Game
, can use it directly, but should use it only for Game specific abstractions. Game
的GameState
实现可以直接使用它,但仅应将其用于特定于游戏的抽象。 The main inconveniences are: 主要的不便是:
GameState
must always be created for one specifc StateManager
. StateManager
创建GameState
。 GameState
implementations such as MainMenu
. GameState
实现(例如MainMenu
中的冗余指针为代价的。 If you have millions of them, it could become a memory issue. If you're not sure whether stateManager
points to a Game
, then use: 如果不确定
stateManager
是否指向Game
,请使用:
Game *game = dynamic_cast<Game*>(stateManager);
game
will then contain a null pointer if stateManager
did not point to a Game
, otherwise it will contain a pointer to the game. 如果
stateManager
没有指向Game
,则game
将包含一个空指针,否则它将包含一个指向游戏的指针。
If you are sure it's always a Game
and want to skip the check (for a tiny performance gain), use: 如果您确信它总是一个
Game
,想跳过检查(一个微小的性能增益),使用方法:
Game *game = static_cast<Game*>(stateManager);
which will produce undefined behaviour if stateManager
doesn't point to a Game
. 如果
stateManager
不指向Game
,它将产生不确定的行为。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.