C++装饰器需用纯虚基类接口、智能指针组合与显式override实现动态增强。基类必须全纯虚以强制重写,装饰器构造函数须接收std::unique_ptr接管所有权,调用链中在next->operation()前后插入增强逻辑。

C++ 没有原生装饰器语法,但用包装类 + 虚函数接口能可靠实现动态增强,关键在于基类接口必须纯虚、所有装饰器必须继承同一抽象基类、构造时传入被装饰对象指针(或智能指针)。
为什么必须用纯虚基类定义接口
装饰器要和被装饰对象“对外表现一致”,就必须共享同一类型。如果基类有非虚函数或默认实现,派生类可能意外绕过装饰逻辑;而纯虚函数强制每个实现(包括 ConcreteComponent 和各个 Decorator)都提供自己的行为版本,确保调用链可控。
常见错误现象:decorator->operation() 调用了基类默认实现,没触发增强逻辑——说明基类中 operation() 不是纯虚函数,或者子类没正确重写。
- 基类声明必须形如:
class Component { public: virtual ~Component() = default; virtual void operation() = 0; }; - 任何具体类(
ConcreteComponent或LoggingDecorator)都必须显式override所有纯虚函数 - 避免在基类中提供非虚的
operation()默认实现
装饰器构造函数必须接收 Component\* 或 std::unique_ptr
这是动态组合的核心:装饰器本身不实现核心逻辑,只负责“包一层”并转发调用。传入原始指针虽简单,但易引发悬空;推荐用 std::unique_ptr 明确所有权转移,避免内存泄漏或重复释放。
使用场景:需要叠加多个装饰器(如 new TimingDecorator(new LoggingDecorator(new ConcreteComponent))),每一层都应接管下层生命周期。
- 错误写法:
LoggingDecorator(Component& comp)—— 引用无法传递所有权,且被装饰对象可能早于装饰器析构 - 推荐写法:
LoggingDecorator(std::unique_ptr,并在构造函数内用next) std::move(next)转移 - 若需共享底层对象(如多装饰器共用一个组件),改用
std::shared_ptr,但要注意循环引用风险
装饰器内部调用 next->operation() 前后插入增强逻辑
这就是“装饰”的实质:在转发前/后加日志、计时、权限校验等。注意不能漏掉对 next 的判空检查(尤其用裸指针时),且增强逻辑必须与原始语义兼容(比如不能擅自修改返回值类型或抛出新异常类型,除非接口契约允许)。
性能影响:每层装饰器增加一次虚函数调用开销 + 一次指针解引用。5 层以内基本无感;若极致追求性能,可考虑 CRTP 静态装饰,但失去运行时组合能力。
- 典型实现:
void LoggingDecorator::operation() override { std::cout operation(); std::cout - 不要在装饰器里直接 new ConcreteComponent —— 违反开闭原则,也破坏组合灵活性
- 若增强逻辑依赖状态(如计数器),确保该状态是装饰器实例私有的,不与其他装饰器共享
std::unique_ptr 组合链的析构顺序容易被忽略
装饰器链是单向的,析构从最外层开始,逐层调用 next 的析构函数。如果某层装饰器的 next 是裸指针且指向栈对象,或多个装饰器共享同一个 std::shared_ptr 但未配对 weak_ptr,就会崩溃。
最容易被忽略的一点:用 std::unique_ptr 构建链后,**最外层装饰器必须是唯一持有者**,不能把它赋给另一个 std::unique_ptr 变量而不 move,否则编译不过;也不能用 .get() 后长期保存裸指针。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











