策略接口应以含虚析构函数的抽象基类定义,方法按需设计参数,避免过度通用;优先用构造函数传参配置,不暴露内部状态,不继承std::function。

策略接口怎么定义才够灵活
策略模式的核心是把算法的定义和使用分开,C++里最直接的方式是用抽象基类作为策略接口。关键不是“要不要虚函数”,而是virtual析构函数必须有——否则通过基类指针删除派生类对象会未定义行为。
常见错误是只声明纯虚函数却忘了virtual ~Strategy() = default;,导致资源泄漏或崩溃。接口方法应按需设计参数,避免过度通用(比如不硬塞std::any或万能模板),多数场景传const std::vector<int>&</int>或double*更清晰。
- 接口只暴露必要操作,例如
execute()、validate(),不暴露内部状态访问器 - 若策略需要配置,优先用构造函数传参,而非运行时
set_config()方法(降低耦合) - 不继承
std::function——它不是策略接口,只是可调用对象包装器
具体策略类如何避免内存管理陷阱
策略对象通常由上下文(Context)持有,这时得明确所有权:用std::unique_ptr<strategy></strategy>最安全,既避免裸指针误删,又比std::shared_ptr少开销。别在栈上临时构造后传地址——生命周期一结束就悬空。
示例中有人写Context c{&QuickSortStrategy{}};,这是典型错误:QuickSortStrategy{}是临时对象,构造完立即析构,c持有的是指向已销毁内存的野指针。
立即学习“C++免费学习笔记(深入)”;
- 始终用智能指针托管策略实例:
std::unique_ptr<strategy> strategy = std::make_unique<quicksortstrategy>();</quicksortstrategy></strategy> - 若策略无状态(比如只含
static成员函数),可考虑用函数指针或std::function替代类,但要清楚这已偏离经典策略模式结构 - 策略类内部避免
new裸指针;真需要动态分配,用std::vector或std::unique_ptr封装
Context类怎么设计才不破坏策略的可替换性
Context不该知道具体策略类型,只依赖Strategy接口。常见反模式是Context里写if (dynamic_cast<quicksortstrategy>(s)) { ... }</quicksortstrategy>——这等于把策略逻辑又拉回上下文,彻底废掉策略模式的意义。
另一个坑是让Context承担策略选择逻辑(比如根据数据规模自动切算法)。这职责应该外移:由工厂或配置层决定用哪个策略,Context只负责执行。
-
Context构造函数接收std::unique_ptr<strategy></strategy>,不提供默认构造+后续set_strategy()(易留空指针) - 不给
Context添加get_strategy_type()之类的方法——类型信息对使用者无意义,且破坏封装 - 若需策略间共享数据,通过构造时传入的
Config对象传递,而非让Context暴露内部状态
编译期策略(std::variant + visit)适合什么场景
当策略种类固定、数量少(比如3–5种)、且性能敏感(避免虚函数调用开销),可用std::variant替代继承体系。但这不是“更高级的策略模式”,而是不同权衡:牺牲扩展性换确定性内联和零虚表开销。
错误用法是把所有策略塞进一个std::variant<quicksort mergesort heapsort></quicksort>,然后每次std::visit都重复写几乎一样的调用逻辑——这反而增加维护成本。
- 仅在策略数稳定、无需运行时增删、且 profiler 确实显示虚调用成瓶颈时才考虑
std::variant - 用
std::visit时统一包装成 lambda,避免重复代码;别在每个分支里写完整业务逻辑 - 不要混合使用:同一模块里既用虚函数策略又用
std::variant策略,会让调用方困惑该用哪种方式切换
if-else更直白。接口膨胀、策略类里塞了大量无关成员变量、Context开始查策略类型……这些信号说明模式正在被滥用。


















