C++中消除Switch语句重复代码的技术方案求助
游戏代码重构:消除冗长Switch与优化内存管理
问题背景
正在重构一款早年开发的平台类游戏,原代码执行效率和内存管理表现糟糕,目前重点精简冗余代码。当前有三个核心痛点:
loadLevel函数依赖冗长switch语句,根据EnumState枚举值调用对应关卡函数goto_level_from_int函数同样用冗长switch实现整数到EnumState的转换- 通过
GameHandler指针传递资源的方式存在内存问题:从暂停界面退出到Windows时,反向遍历先前状态会崩溃
现有代码示例
EnumState定义(EnumState.h)
enum EnumState { main_menu, level_select_screen, lvl_1, lvl_2, lvl_3, //... 更多枚举值 };
loadLevel函数
void loadLevel(EnumState state, GameHandler *handler) { switch(state) { case main_menu: mainMenu(handler); break; case lvl_1: lvl1(handler); break; case lvl_2: lvl2(handler); break; //... 以此类推 } }
goto_level_from_int函数
EnumState goto_level_from_int(int whatLevel) { switch(whatLevel) { case 1: return lvl_1; break; case 2: return lvl_2; break; //... 以此类推 } }
需求是实现类似字符串拼接调用函数的逻辑(比如lvl_ + levelNumber + (handler)),希望用C/C++特性替代switch,同时解决内存管理问题。
解决方案
1. 用数组替代switch(高效且简洁)
因为EnumState默认是连续枚举值(从0开始),可以直接用数组映射函数指针和整数到枚举的转换:
替换loadLevel的数组方案
首先定义关卡函数的类型:
using LevelFunc = void(*)(GameHandler*);
创建全局/静态的函数映射数组,顺序必须和EnumState枚举完全对应:
const LevelFunc levelFuncs[] = { mainMenu, // 对应main_menu levelSelect, // 对应level_select_screen lvl1, // 对应lvl_1 lvl2, // 对应lvl_2 lvl3, // 对应lvl_3 //... 按枚举顺序添加所有关卡函数 };
修改后的loadLevel函数:
void loadLevel(EnumState state, GameHandler *handler) { // 先判断状态是否合法 if (state >= 0 && state < sizeof(levelFuncs)/sizeof(LevelFunc)) { levelFuncs[state](handler); } else { // 非法状态默认跳转主菜单 mainMenu(handler); } }
替换goto_level_from_int的数组方案
如果关卡整数和枚举值有直接对应关系(比如关卡N对应lvl_N),可以直接计算:
EnumState goto_level_from_int(int whatLevel) { // 校验关卡号合法性 if (whatLevel >=1 && whatLevel <= MAX_LEVEL_COUNT) { return static_cast<EnumState>(lvl_1 + whatLevel -1); } else { return main_menu; } }
如果枚举值不连续,或对应关系复杂,用数组映射:
const EnumState intToLevel[] = { main_menu, // 索引0对应非法值 lvl_1, // 索引1对应关卡1 lvl_2, // 索引2对应关卡2 lvl_3 // 索引3对应关卡3 }; EnumState goto_level_from_int(int whatLevel) { if (whatLevel >=1 && whatLevel < sizeof(intToLevel)/sizeof(EnumState)) { return intToLevel[whatLevel]; } return main_menu; }
2. 用std::map实现灵活映射(适合非连续枚举)
如果EnumState枚举值不连续,或需要动态添加关卡,用std::map更合适:
替换loadLevel的map方案
#include <map> std::map<EnumState, LevelFunc> levelFuncMap = { {main_menu, mainMenu}, {level_select_screen, levelSelect}, {lvl_1, lvl1}, {lvl_2, lvl2}, //... 添加所有状态-函数映射 }; void loadLevel(EnumState state, GameHandler *handler) { auto it = levelFuncMap.find(state); if (it != levelFuncMap.end()) { it->second(handler); } else { mainMenu(handler); } }
替换goto_level_from_int的map方案
std::map<int, EnumState> intToLevelMap = { {1, lvl_1}, {2, lvl_2}, {3, lvl_3}, //... 添加所有关卡号-枚举映射 }; EnumState goto_level_from_int(int whatLevel) { auto it = intToLevelMap.find(whatLevel); if (it != intToLevelMap.end()) { return it->second; } return main_menu; }
3. 内存管理优化建议
退出崩溃大概率是资源重复释放、悬空指针或状态栈管理混乱导致的,可按以下方式优化:
- 改用智能指针(
std::unique_ptr/std::shared_ptr)管理GameHandler及内部资源,避免悬空指针 - 维护一个状态栈(比如
std::stack<EnumState>)记录状态切换历史,退出时按栈顺序释放对应资源,确保每个状态的资源只被释放一次 - 每个关卡函数负责初始化自身资源,退出关卡时主动清理;或在
GameHandler中给资源添加引用计数,计数为0时自动释放 - 退出到Windows前,清空状态栈,依次调用每个状态的清理函数,确保所有资源被正确释放
注意事项
- 数组方案要求枚举值顺序和数组元素完全同步,新增枚举时必须更新数组
- map方案首次访问有轻微性能开销,但关卡切换属于低频操作,完全可忽略
- 映射结构(数组/map)建议在程序启动时初始化一次,避免重复创建
内容的提问来源于stack exchange,提问作者gabelossus
相关产品推荐
相关产品推荐

