装饰者模式在c++中本质是接口继承加组合:所有组件与装饰器均继承同一纯虚基类(如component),装饰器通过std::unique_ptr持有被装饰对象,调用时先执行自身逻辑再委托给内部对象,构造顺序决定执行逆序,且必须严格管理所有权以避免内存泄漏或悬挂指针。

装饰者模式在C++里本质是「接口继承 + 组合」
不是靠宏、模板特化或运行时注入,而是用虚函数接口定义行为契约,再通过组合持有被装饰对象。关键在于所有装饰器和原始类都实现同一抽象基类(比如 Component),且装饰器内部保存一个 std::unique_ptr<component></component> 或裸指针指向被装饰对象。
常见错误是让装饰器直接继承具体类(如 FileLogger → TimestampLogger),这破坏了开闭原则,也失去多层嵌套能力。正确做法是:所有功能扩展都面向接口,而非实现。
-
Component是纯虚基类,只声明operation()等核心接口 - 每个装饰器(如
CompressionDecorator)继承Component,并在构造时接收一个std::unique_ptr<component></component> - 装饰器的
operation()先调用被装饰对象的operation(),再叠加自己的逻辑(如压缩、日志、缓存)
如何避免内存泄漏和悬挂指针
装饰器链中对象生命周期必须清晰。推荐统一用 std::unique_ptr 管理所有权,且由最外层装饰器负责释放整个链。不要混用裸指针和智能指针——尤其当装饰器需要传递给其他模块时,裸指针极易导致提前释放或重复释放。
典型坑:CompressionDecorator 构造时接收一个临时 std::unique_ptr,但内部存为裸指针;后续原指针释放后,装饰器还试图调用它,触发未定义行为。
- 所有装饰器成员变量用
std::unique_ptr<component></component>,不存裸指针 - 构造函数参数也用
std::unique_ptr<component></component>,并用std::move转移所有权 - 若需共享底层对象(如多个装饰器共用一个
FileWriter),改用std::shared_ptr,但要警惕循环引用
多层装饰时的调用顺序怎么控制
装饰顺序 = 构造顺序的逆序。因为最外层装饰器最先被构造,它包裹的是内层装饰器;而执行时,最外层的 operation() 会先做自己的事,再调用内层的 operation() —— 所以「外层先执行前置逻辑,内层后执行」。
例如:auto logger = std::make_unique<timestampdecorator>(std::make_unique<filedecorator>(std::make_unique<consolewriter>()));</consolewriter></filedecorator></timestampdecorator>,调用 logger->write("msg") 时,执行流是:TimestampDecorator::write → FileDecorator::write → ConsoleWriter::write。
- 想加时间戳再写文件?就让
TimestampDecorator包裹FileDecorator - 想先写文件再打日志?就得把
FileDecorator放在更外层,或者重构成支持钩子的结构 - 没有运行时切换顺序的能力;顺序在构造时固定,若需动态调整,得配合工厂或策略模式
要不要用模板实现静态装饰器
可以,但不是“装饰者模式”的本意。模板版本(如 CompressedWriter<bufferedwriter>></bufferedwriter>)在编译期展开,零开销,但丧失运行时组合灵活性,也无法统一类型(每个嵌套深度都是不同类型)。
真实项目中,如果你确定功能组合固定、无配置需求、且对虚函数调用敏感(如高频日志),模板方案可行;否则坚持虚函数接口方案,它能被容器持有、可序列化配置、支持插件式加载。
- 模板装饰器无法放入
std::vector<:unique_ptr>></:unique_ptr>,因为类型不一致 -
dynamic_cast在虚函数方案中可用(比如判断是否支持flush()),模板方案做不到 - 调试时,虚函数调用栈清晰可见;模板展开后的符号名可能极长,gdb 里难定位
装饰者模式真正难的不是写几层继承,而是厘清谁拥有对象、谁负责释放、以及哪一层该做哪件事。一旦装饰链跨线程或涉及异步回调,所有权和生命周期问题会立刻浮出水面——这时候,连 std::shared_ptr 都不一定够用,得配合明确的生命周期协议。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











