策略接口必须是抽象类且含纯虚函数,Context通过智能指针持有策略以避免切片,std::function可替代但不具策略族语义,策略不得访问Context私有成员。

策略接口必须是抽象类或纯虚函数
策略模式的核心是把算法的定义和使用分离,C++里最直接的方式就是用抽象基类声明接口,不能用普通结构体或具名函数指针替代——否则无法多态调用。常见错误是写成带实现的虚函数,导致子类忘记重写却无编译报错。
- 接口类必须至少有一个
virtual ~Strategy() = default;析构函数,否则通过基类指针 delete 派生对象会未定义行为 - 算法方法必须声明为
virtual void execute() const = 0;,强制子类实现 - 避免在基类中提供默认实现(比如空函数体),这会让策略“可选”失去约束力
Context 类持有 Strategy 指针而非值对象
Context 要能动态切换策略,所以必须用指针或智能指针管理 Strategy 实例,不能按值存储——否则发生切片(slicing),派生类特有逻辑全丢。
- 推荐用
std::unique_ptr<strategy></strategy>,明确所有权;若需共享策略实例,改用std::shared_ptr<strategy></strategy> - 构造函数接受
std::unique_ptr<strategy>&&</strategy>或std::shared_ptr<strategy></strategy>,避免裸指针裸传 - 不要在 Context 内部 new 具体策略类——那会把策略选择逻辑耦合进 Context,违背开闭原则
std::function 替代继承也能实现策略,但语义不同
用 std::function<void></void> 存储可调用对象(lambda、函数指针、绑定表达式)确实能快速实现行为替换,但它不是严格意义上的策略模式——没有策略族的类型体系,也无法扩展策略自身的状态或配置。
- 适合简单场景:比如日志输出方式切换(
console_log/file_log),用std::function更轻量 - 不适用复杂策略:比如一个
PaymentStrategy需要保存订单号、签名密钥、重试次数,这时必须用类继承 + 成员变量 - 性能差异小,但
std::function有小概率触发堆分配(对短 lambda 通常优化掉了)
别在策略里访问 Context 的私有成员
策略类设计初衷是独立可测试、可复用,一旦它开始依赖 Context 的内部实现(比如直接读 Context::data_),就破坏了封装,也导致策略无法脱离 Context 单元测试。
立即学习“C++免费学习笔记(深入)”;
- Context 应通过参数把所需数据传给
execute(),例如virtual void execute(const InputData& input) = 0; - 如果策略需要回调 Context 的某些能力(如记录日志、触发事件),应由 Context 提供接口(如
Logger& get_logger()),并在构造时注入 - 常见反模式:
class ConcreteStrategy : public Strategy { Context* ctx_; };—— 这让策略变成 Context 的附属品
策略模式真正难的不是语法,是判断哪些逻辑该放进策略、哪些该留在 Context。边界模糊时,优先让策略只接收输入、返回输出,其余都交给 Context 编排。


















