C++中如何优化同签名相似功能函数的调用代码?
我有一段调用多个同签名、功能相似但实现不同的函数的代码:
{ if(!calcFunc1(sameTypeA, sameTypeB) || !calcFunc2(sameTypeB, sameTypeC) || !calcFunc3(sameTypeB, sameTypeD) || !calcFunc4(sameTypeC, sameTypeE) // more ... ) { // some error handling } }
这些函数的签名如下:
bool calcFunc1(const MyClass& classInput, MyClass& classOutput) const; bool calcFunc2(const MyClass& classInput, MyClass& classOutput) const; bool calcFunc3(const MyClass& classInput, MyClass& classOutput) const; // more ...
它们是处理数据包的不同算法,每次用自身技术让数据更精准,得到更小的子集。理论上前一个函数的输出是下一个的输入,但也存在例外情况(如calcFunc2和calcFunc3的输入)。
我希望让这段代码更优雅简洁,使用环境至少为C11,不确定是否可用C17,仅能使用std或Boost库。如果是类的话我会考虑多态,想知道函数是否有类似方案,或者是否应该使用抽象类,期待任何优化建议。
举个例子:假设MyClass是图像,目标是提取字母,每个calcFunc尝试提取字母,sameType是感兴趣区域(maskedImage),它们还会用到初始未处理的图像副本。这些算法提取时较为保守,避免丢失重要数据,仅剔除明显无关部分。同时希望未来开发者能轻松添加、修改或调整这些函数的顺序。比如calcFunc5可让感兴趣区域更清晰,找回丢失的线条末端;calcFunc6再过滤掉calcFunc5添加的无关内容。
1. 函数列表+结构化任务配置(C++11兼容)
针对输入输出不固定顺序的情况,不用强行串流水线,而是把每个任务的函数、输入、输出打包成结构体,放在列表里遍历执行。新增或调整算法只需要修改列表,不用动核心逻辑。
先定义任务结构体:
#include <vector> #include <functional> struct CalcTask { std::function<bool(const MyClass&, MyClass&)> func; const MyClass& input; MyClass& output; };
重构调用逻辑:
// 按执行顺序初始化任务列表 std::vector<CalcTask> tasks = { {calcFunc1, sameTypeA, sameTypeB}, {calcFunc2, sameTypeB, sameTypeC}, {calcFunc3, sameTypeB, sameTypeD}, {calcFunc4, sameTypeC, sameTypeE}, // 新增算法直接追加到这里 }; bool allSuccess = true; for (const auto& task : tasks) { if (!task.func(task.input, task.output)) { allSuccess = false; break; // 可根据需求决定是否终止后续任务 } } if (!allSuccess) { // 错误处理 }
这种方式最直观,C++11完全支持,后续维护成本极低,开发者只需调整tasks列表即可。
2. 策略模式(抽象类+多态)
如果算法需要依赖外部状态(比如示例中的初始图像副本),用抽象类封装更合适,能让每个算法的逻辑更独立。
先定义基类:
class CalcStrategy { protected: const MyClass& originalImage; // 保存初始图像供算法使用 public: CalcStrategy(const MyClass& orig) : originalImage(orig) {} virtual ~CalcStrategy() = default; // 虚析构保证正确销毁 virtual bool process(const MyClass& input, MyClass& output) const = 0; };
每个算法实现为子类:
class CalcFunc1 : public CalcStrategy { public: using CalcStrategy::CalcStrategy; bool process(const MyClass& input, MyClass& output) const override { // 直接使用originalImage,实现原calcFunc1逻辑 } }; // 同理实现CalcFunc2、CalcFunc3等子类
使用时创建策略对象列表,按需关联输入输出:
const MyClass originalImage = ...; // 初始未处理图像 std::vector<std::unique_ptr<CalcStrategy>> strategies; strategies.emplace_back(new CalcFunc1(originalImage)); strategies.emplace_back(new CalcFunc2(originalImage)); MyClass a, b, c, d, e; bool success = true; success &= strategies[0]->process(a, b); success &= strategies[1]->process(b, c); // 复杂输入输出关联可结合前面的任务结构体管理
这种方式符合开闭原则,新增算法只需添加子类,封装性更强。
3. Boost库辅助函数绑定(若允许使用Boost)
如果需要给现有函数绑定固定参数(比如初始图像),不用修改原函数签名,可借助Boost.Bind实现:
#include <boost/bind.hpp> #include <boost/function.hpp> // 假设原算法需要用到初始图像,定义带原图像参数的函数 bool calcFunc1WithOriginal(const MyClass& original, const MyClass& input, MyClass& output) { // 使用original和input处理得到output } // 绑定初始图像参数,生成符合原签名的函数 const MyClass original = ...; auto boundFunc1 = boost::bind(calcFunc1WithOriginal, boost::cref(original), _1, _2); // 后续可将boundFunc1加入任务列表使用
这种方式适配现有代码更快捷,无需大规模重构。
额外建议
- 若需运行时动态调整算法顺序,可将任务列表初始化逻辑抽离到单独函数或配置模块,与业务逻辑解耦。
- 错误处理可细化:给任务结构体增加名称字段,记录失败的任务名称,方便调试。
- 若可用C17,可借助
std::variant或结构化绑定简化代码,但C11的方案已足够满足需求。
内容的提问来源于stack exchange,提问作者kaziko ni

