从抽象基础 class 指针转换为派生 class
Casting from abstract base class pointer to derived class
我正在尝试为我的游戏创建一个状态管理器,我有 4 个 classes:
游戏状态:
// 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() { }
};
状态管理器:
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);
}
}
和主菜单:
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
中的 class Game
的唯一方法是通过转换指针?
// MainMenu class
void update(StateManager* stateManager)
{
Game* game = (Game*) stateManager;
game.input.getKey(ANY_KEY);
//...
}
这样对吗?有些东西告诉我我做错了。
如果您不确定 stateManager
是否指向 Game
,请使用:
Game *game = dynamic_cast<Game*>(stateManager);
如果 stateManager
没有指向 Game
,game
将包含一个空指针,否则它将包含一个指向游戏的指针。
如果您确定它总是一个Game
并且想跳过检查(以获得微小的性能提升) , 使用:
Game *game = static_cast<Game*>(stateManager);
如果 stateManager
不指向 Game
.
将产生未定义的行为
immibis 的答案非常适合解决技术铸造问题。
不过,您的设计有些奇怪。所以我想提供一个替代答案,解决设计问题。
首先 StateManager
本身不是 GameState
。因此 update()
和 draw()
不需要相同的签名。您是否曾预见到这些 StateManager
函数中的一个会在参数中与另一个 StateManager
一起调用?对于Game
,我认为这没有意义。所以我建议重构 class(并相应地调整 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
抽象。
GameState
的具体实现依赖于 Game
,可以直接使用它,但应该只将它用于特定于游戏的抽象。
主要不便之处有:
GameState
必须始终为一个特定的 StateManager
创建。
- 设计的稳健性是以所有
GameState
实现(例如 MainMenu
)中的冗余指针为代价的。如果你有数百万个,它可能会成为一个内存问题。
我正在尝试为我的游戏创建一个状态管理器,我有 4 个 classes:
游戏状态:
// 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() { }
};
状态管理器:
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);
}
}
和主菜单:
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
中的 class Game
的唯一方法是通过转换指针?
// MainMenu class
void update(StateManager* stateManager)
{
Game* game = (Game*) stateManager;
game.input.getKey(ANY_KEY);
//...
}
这样对吗?有些东西告诉我我做错了。
如果您不确定 stateManager
是否指向 Game
,请使用:
Game *game = dynamic_cast<Game*>(stateManager);
如果 stateManager
没有指向 Game
,game
将包含一个空指针,否则它将包含一个指向游戏的指针。
如果您确定它总是一个Game
并且想跳过检查(以获得微小的性能提升) , 使用:
Game *game = static_cast<Game*>(stateManager);
如果 stateManager
不指向 Game
.
immibis 的答案非常适合解决技术铸造问题。
不过,您的设计有些奇怪。所以我想提供一个替代答案,解决设计问题。
首先 StateManager
本身不是 GameState
。因此 update()
和 draw()
不需要相同的签名。您是否曾预见到这些 StateManager
函数中的一个会在参数中与另一个 StateManager
一起调用?对于Game
,我认为这没有意义。所以我建议重构 class(并相应地调整 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
抽象。GameState
的具体实现依赖于Game
,可以直接使用它,但应该只将它用于特定于游戏的抽象。
主要不便之处有:
GameState
必须始终为一个特定的StateManager
创建。- 设计的稳健性是以所有
GameState
实现(例如MainMenu
)中的冗余指针为代价的。如果你有数百万个,它可能会成为一个内存问题。