C++装饰器模式必须通过组合与虚函数实现,核心是定义纯虚基类(如Component),所有具体组件和装饰器均继承它,装饰器内部持std::unique_ptr<Component>成员并委托调用;禁止继承具体类或使用裸指针,需用移动语义构建装饰链以确保所有权安全。

装饰器模式在C++里没有@语法,得靠组合和虚函数
C++没有Python那种@decorator语法糖,实现装饰器模式必须手动建类、定义接口、用指针/引用组合被装饰对象。核心是「不修改原类,通过包装增加行为」——这要求被装饰类和装饰器实现同一抽象接口,且装饰器内部持有一个该接口的指针(通常是std::unique_ptr或裸指针)。
常见错误是直接继承原类并加新方法,那叫子类扩展,不是装饰器;或者忘了让装饰器也继承同一基类,导致无法透明替换。
- 必须定义纯虚基类(如
Component),所有功能操作都声明为虚函数 - 具体组件(
ConcreteComponent)和所有装饰器(Decorator及其子类)都继承它 - 装饰器构造函数接收
std::unique_ptr<component></component>或Component*,并保存为成员 - 装饰器的虚函数实现中,先调用被包装对象的对应方法(
component_->operation()),再叠加自己的逻辑
如何写一个可叠加的装饰器链(比如日志+缓存+验证)
装饰器能嵌套的关键,在于每个装饰器本身也是Component,所以可以传给下一个装饰器构造函数。但要注意所有权和生命周期:推荐用std::unique_ptr自动管理,避免裸指针悬空。
典型错误是把装饰器当成值对象传递(比如LoggingDecorator{obj}),导致临时对象析构后内部指针失效;或者多个装饰器共享同一原始对象指针,却没处理线程安全或状态污染。
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 用移动语义构造链:
auto ptr = std::make_unique<loggingdecorator>(std::make_unique<cachedecorator>(std::make_unique<authdecorator>(std::make_unique<concretecomponent>())));</concretecomponent></authdecorator></cachedecorator></loggingdecorator> - 每个装饰器的
operation()里,先调用component_->operation(),再执行自身逻辑(比如记录日志、查缓存) - 如果装饰器需要修改行为逻辑(如缓存命中时跳过下游),就不要无条件调用
component_->operation(),而是按需分支 - 避免在装饰器中暴露被包装对象的原始类型(比如提供
get_raw()),否则破坏封装性
std::shared_ptr vs std::unique_ptr:选哪个管理装饰器链?
绝大多数场景该用std::unique_ptr。装饰器链本质是单所有权的包装结构,std::shared_ptr会引入不必要的原子计数开销,还容易因循环引用导致内存泄漏(比如装饰器内部又存了对自身的shared_ptr)。
只有当你需要从外部某处临时观察链中某个节点(比如调试时打印当前装饰器类型),且不希望影响主链生命周期,才考虑用std::weak_ptr配合shared_ptr——但这已超出装饰器模式本意,属于额外需求。
-
std::unique_ptr:默认选择,性能好、语义清晰、防误用 - 若必须共享(如多线程并发调用同一装饰器实例),确保所有装饰器实现是线程安全的(比如缓存读写加锁),而不是靠
shared_ptr解决 - 绝对不要在装饰器构造函数里用
shared_ptr捕获this,这是经典循环引用陷阱
为什么不用模板实现静态装饰器(类似CRTP)?
可以用,但会失去运行时动态组合能力。CRTP装饰器(如template<typename t> class LoggingDecorator : public T</typename>)在编译期就确定了装饰顺序和类型,无法做到「根据配置加载不同装饰器」或「运行时切换缓存策略」。
而且CRTP会让接口难以统一:每个装饰后的类型都不同,没法放进std::vector<:unique_ptr>></:unique_ptr>,也无法被同一函数参数接受。
- 静态装饰器适合零成本抽象、确定不变的增强(如为所有
Widget自动加构造日志) - 但只要涉及策略选择、配置驱动、或需要多态容器持有,就必须用虚函数+动态分配的方案
- 混合使用(CRTP做基础增强 + 虚函数装饰器做策略层)可行,但会显著增加理解和维护成本
operator=和拷贝构造函数,否则可能意外复制出指向已销毁对象的指针。

















