模板方法模式骨架:基类中templateMethod()为public non-virtual,封装固定流程;doStep1()等钩子为protected virtual,可选加=0或final;禁止虚化templateMethod()以保骨架不变。

模板方法模式在 C++ 里怎么写骨架?
用 virtual + final 组合控制流程入口和扩展点,基类定义算法骨架,子类只重写可变部分。核心不是“怎么继承”,而是“哪些函数必须被重写”“哪些绝对不能被重写”。
-
templateMethod()是公有非虚函数,封装固定流程,内部调用若干virtual钩子 - 钩子函数(如
doStep1()、doStep2())声明为protected virtual,允许子类覆盖 - 若某步逻辑不允许子类修改(比如资源清理顺序),就用
final修饰该虚函数,或直接写成private+final的非虚函数
为什么不能把 templateMethod() 声明为 virtual?
一旦 templateMethod() 是虚函数,子类就能彻底替换整个算法——这违背模板方法的本意:骨架不变,细节可变。常见错误是初学者把所有函数都设成 virtual,结果子类一重写就跳过校验、跳过日志、甚至绕过初始化。
- 典型现象:
Base::templateMethod()调用了init()和validate(),但子类重写了templateMethod(),这两步直接消失 - 正确做法:基类中
templateMethod()是public、non-virtual,确保流程强制执行 - C++11 起推荐用
final显式禁止覆盖关键步骤,比靠文档提醒更可靠
纯虚函数 vs 默认实现的钩子函数怎么选?
取决于该步骤是否“必须由子类提供逻辑”。不是语法问题,是设计契约问题。
- 纯虚函数(
virtual void doWork() = 0;):表示该步骤无默认行为,子类不实现就链接失败 - 带空实现的虚函数(
virtual void onDone() {}):表示可选回调,子类按需覆盖,不影响主流程 - 带默认实现的虚函数(
virtual std::string getName() { return "default"; }):提供安全兜底,避免子类忘记返回值导致未定义行为 - 注意:不要在纯虚函数里写实现体,C++ 允许但语义混乱,容易让维护者误以为“可以不重写”
std::unique_ptr 调用 templateMethod() 会出问题吗?
不会,只要基类有虚析构函数,多态调用完全安全。但要注意对象生命周期和模板方法执行时机。
立即学习“C++免费学习笔记(深入)”;
- 常见坑:在构造函数里调用
templateMethod()—— 此时子类成员还未初始化,虚函数调用会绑定到基类版本(C++ 对象构造期间虚表未就绪) - 另一个坑:用
std::move后再调用templateMethod(),如果内部访问了已转移的资源(如std::vector成员),会触发未定义行为 - 性能影响极小:现代编译器对 final 函数或单级虚调用常做去虚化(devirtualization),实际开销接近普通函数调用
= 0,背后是接口契约的设计权衡,不是“先写出来再改”。










