无法在std::map中正确存取GameState对象,多态调用失效求助
问题原因分析
你的问题核心是对象切片(Object Slicing),这是C++处理多态时极易踩中的坑。
看这段关键代码:
gameStates.insert(std::make_pair(name, *state));
你把传入的GameState*指针解引用后,直接将子类对象(比如MainMenuState)赋值给了std::map里的GameState父类对象。此时子类特有的成员变量、虚函数表信息都会被"切掉",map最终存储的只是一个纯粹的GameState父类副本,完全丢失了子类的多态特性。
后续从map中取出对象赋值给nextState和currentState时,拿到的依然是被切片后的父类对象,所以调用HandleInput()、Update()、Draw()这些虚函数时,只会执行父类的默认实现,永远触发不了子类的重写版本。
解决思路
要保留多态特性,你需要在map中存储**指针(或智能指针)**而非对象本身,这样才能通过指针的动态绑定,正确调用子类的虚函数实现。
1. 修改容器类型
把gameStates改成存储智能指针(推荐用std::unique_ptr,自动管理内存,避免泄漏):
std::map<std::string, std::unique_ptr<GameState>> gameStates{};
2. 更新AddGameState方法
调整插入逻辑,直接存储子类对象的指针(用智能指针接管内存):
void GameStateManager::AddGameState(std::string name, GameState* state) { // 用unique_ptr接管传入的state内存,无需手动释放 gameStates.emplace(std::move(name), std::unique_ptr<GameState>(state)); // C++14及以上更推荐用make_unique直接创建对象,避免裸指针传递: // gameStates.emplace(std::move(name), std::make_unique<MainMenuState>()); }
3. 修改状态切换相关代码
把nextState和currentState也改成智能指针类型,确保调用虚函数时触发动态绑定:
// 在GameStateManager类中声明成员变量 std::unique_ptr<GameState> nextState; std::unique_ptr<GameState> currentState; void GameStateManager::SetNext(std::string name) { auto it = gameStates.find(name); if (it != gameStates.end()) { // 如果需要每个状态是独立实例,建议给GameState加纯虚Clone方法: // nextState = it->second->Clone(); // 如果共享同一个实例,直接赋值即可: nextState = it->second; } } void GameStateManager::SwitchState() { currentState = std::move(nextState); }
4. 调用虚函数的调整
现在currentState是智能指针,调用成员函数时需要用->:
currentState->HandleInput(); currentState->Update(); currentState->Draw();
额外提示
- 如果状态对象需要多次复用,建议给
GameState类添加纯虚的Clone()方法,每次切换状态时创建子类对象的新副本,避免共享实例导致的状态混乱。 - 尽量避免裸指针,优先用
std::unique_ptr或std::shared_ptr管理内存,减少内存泄漏风险。
内容的提问来源于stack exchange,提问作者Caedendi
相关产品推荐
相关产品推荐

