在构造函数中条件定义类的函数调用行为
解决方法
你可以通过在类构造阶段就绑定要调用的目标函数,避免每次执行run方法时的条件判断,以下是几种常用的实现方案:
1. 存储函数指针
在类中声明一个匹配候选函数签名的函数指针成员,构造时根据传入的参数直接初始化该指针,run方法里直接调用指针即可:
void functionCandidate1(const Type1 & arg1, Type2 & arg2) {/* 实现逻辑 */} void functionCandidate2(const Type1 & arg1, Type2 & arg2) {/* 不同的实现逻辑 */} class MyClass{ using FuncPtr = void(*)(const Type1&, Type2&); FuncPtr target_func_; public: MyClass(bool use_function1) : target_func_(use_function1 ? functionCandidate1 : functionCandidate2) {} void run(const Type1 & arg1){ Type2 obj; target_func_(arg1, obj); } };
- 优点:实现简单直接,无额外运行时开销,性能高效。
- 缺点:仅支持绑定非成员函数或静态成员函数,灵活性有限。
2. 使用std::function
如果需要支持更灵活的可调用对象(比如lambda表达式、绑定的成员函数),可以用std::function替代原始函数指针:
#include <functional> void functionCandidate1(const Type1 & arg1, Type2 & arg2) {/* 实现逻辑 */} void functionCandidate2(const Type1 & arg1, Type2 & arg2) {/* 不同的实现逻辑 */} class MyClass{ std::function<void(const Type1&, Type2&)> target_func_; public: MyClass(bool use_function1) : target_func_(use_function1 ? functionCandidate1 : functionCandidate2) {} void run(const Type1 & arg1){ Type2 obj; target_func_(arg1, obj); } };
- 优点:支持多种可调用类型,适配场景更广。
- 缺点:相比原始函数指针有极轻微的运行时开销(多数场景下可忽略)。
3. 模板类(编译期确定逻辑)
如果可以在编译期确定要使用的函数,用模板参数替代运行时变量,完全消除运行时判断:
方案A:使用if constexpr(C++17及以上)
void functionCandidate1(const Type1 & arg1, Type2 & arg2) {/* 实现逻辑 */} void functionCandidate2(const Type1 & arg1, Type2 & arg2) {/* 不同的实现逻辑 */} template<bool UseFunction1> class MyClass{ public: void run(const Type1 & arg1){ Type2 obj; if constexpr(UseFunction1) { // 编译期分支消除,无运行时判断 functionCandidate1(arg1, obj); } else { functionCandidate2(arg1, obj); } } }; // 使用方式 MyClass<true> obj1; // 绑定functionCandidate1 MyClass<false> obj2; // 绑定functionCandidate2
方案B:模板特化
void functionCandidate1(const Type1 & arg1, Type2 & arg2) {/* 实现逻辑 */} void functionCandidate2(const Type1 & arg1, Type2 & arg2) {/* 不同的实现逻辑 */} template<bool UseFunction1> class MyClass; // 特化使用functionCandidate1的版本 template<> class MyClass<true>{ public: void run(const Type1 & arg1){ Type2 obj; functionCandidate1(arg1, obj); } }; // 特化使用functionCandidate2的版本 template<> class MyClass<false>{ public: void run(const Type1 & arg1){ Type2 obj; functionCandidate2(arg1, obj); } };
- 优点:编译期确定调用逻辑,无任何运行时开销,性能最优。
- 缺点:必须在编译期确定使用的函数,无法在运行时动态切换。
4. 继承与多态
如果未来需要扩展更多候选函数,多态是更具扩展性的方案:
#include <memory> void functionCandidate1(const Type1 & arg1, Type2 & arg2) {/* 实现逻辑 */} void functionCandidate2(const Type1 & arg1, Type2 & arg2) {/* 不同的实现逻辑 */} class MyBaseClass{ public: virtual ~MyBaseClass() = default; virtual void run(const Type1 & arg1) = 0; }; class MyClass1 : public MyBaseClass{ public: void run(const Type1 & arg1) override { Type2 obj; functionCandidate1(arg1, obj); } }; class MyClass2 : public MyBaseClass{ public: void run(const Type1 & arg1) override { Type2 obj; functionCandidate2(arg1, obj); } }; // 工厂函数,根据参数创建对应子类对象 std::unique_ptr<MyBaseClass> createMyClass(bool use_function1) { return use_function1 ? std::make_unique<MyClass1>() : std::make_unique<MyClass2>(); }
- 优点:扩展性强,新增候选函数只需添加新的子类;支持运行时动态切换逻辑。
- 缺点:存在虚函数调用的轻微开销,代码结构相对复杂。
内容的提问来源于stack exchange,提问作者Morten Nissov
相关产品推荐
相关产品推荐

