函数指针数组适用于离散有限整型枚举判断,需确保函数签名一致、显式初始化、越界检查;std::map/std::unordered_map适合字符串条件分支,推荐string_view和判空;虚函数+工厂模式替代类型判断,基类析构必须virtual;非必要不重构简单if-else。

用函数指针数组替代多层if-else分支
当判断条件是**离散、有限、类型一致的整型枚举或索引**(比如状态码 0~5),函数指针数组是最轻量、最直接的替代方案。它把“判断→跳转”变成“查表→调用”,避免逐个比较。
常见错误是把指针数组声明成 void (*)() 却传入带参数的函数,导致调用崩溃;或者没做越界检查,用非法索引访问数组。
- 确保所有函数签名完全一致,例如统一为
int (*handlers[6])(const Data&) - 初始化时显式写全每个元素,别依赖默认零值:
handlers[STATE_INIT] = &handle_init; - 访问前必须校验索引:
if (state = 6) return ERROR_INVALID_STATE; - 编译期可考虑用
std::array替代裸数组,获得.size()和范围检查支持
用std::map<:string std::function>>处理字符串条件分支
当原始 if-else 是基于字符串匹配(如命令名 "save"、"load"、"quit")时,硬编码 strcmp 或 switch+hash 手动映射容易漏项、难维护。用 std::map + std::function 能让逻辑清晰且支持运行时注册。
性能上,std::map 是 O(log n) 查找,小规模(≤20 项)不如 std::unordered_map 的平均 O(1),但后者需自定义哈希;若全部键在编译期已知,std::unordered_map 初始化开销略高。
- 键建议用
std::string_view(C++17+)减少临时构造:std::unordered_map<:string_view std::function>> cmd_map;</:string_view> - 注册时别写错函数对象:用
cmd_map["save"] = [&]() { save_to_disk(); };,而不是cmd_map["save"] = save_to_disk;(除非函数签名完全匹配) - 查找后务必判空:
auto it = cmd_map.find(cmd); if (it != cmd_map.end()) it->second();
用虚函数+工厂模式替换基于类型的if-else
当 if-else 在判断对象具体类型并调用不同行为(如 if (obj.type == TYPE_A) obj.do_a(); else if (obj.type == TYPE_B) obj.do_b();),这本质是违反开闭原则的典型场景。虚函数加基类指针/引用才是 C++ 的自然解法。
容易踩的坑是工厂返回了栈对象地址,或忘记将基类析构函数声明为 virtual,导致派生类资源泄漏。
- 基类必须有
virtual ~Base() = default;,哪怕空实现 - 工厂函数返回
std::unique_ptr<base>,避免裸指针管理负担 - 不要在基类里塞大量
dynamic_cast回退逻辑——那说明设计倒退回 if-else 思维 - 若类型极少且固定,可考虑
std::variant+std::visit,但需 C++17 支持
什么时候不该强行用指针替代if-else
不是所有 if-else 都值得重构。编译器对简单条件分支(尤其是 if (x == 1) ... else if (x == 2) ...)会自动优化为跳转表或二分比较,手写指针反而增加间接跳转开销和 cache miss 概率。
真正该动手的,是那些嵌套深、分支多、易扩展、或逻辑分散在多处的判断块。如果替换后代码更难读懂、调试更费劲、或者引入了悬垂指针/未初始化函数指针,那就停手。
复杂点在于:函数指针不能捕获局部变量,lambda 又受限于生命周期;std::function 有类型擦除开销;虚函数调用有 vtable 查找成本。选哪个,得看你的 hot path 瓶颈在哪,而不是“看起来更高级”。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











