c++的装饰器模式如何实现 不修改原有代码扩展功能【设计模式】

C++可通过装饰器模式实现类似Python @decorator的功能,核心是组合优于继承:定义抽象接口Component,ConcreteComponent和Decorator均实现它,Decorator持std::unique_ptr转发调用并添加逻辑;支持运行时链式叠加;模板+CRTP可实现零开销静态装饰。

C++ 本身没有像 Python 那样的原生 @decorator 语法,但装饰器模式(Decorator Pattern)完全可以用标准 C++ 实现——核心是“组合优于继承”,通过包装对象,在不修改原始类代码的前提下动态添加行为。

用抽象接口 + 具体装饰器实现扩展

关键在于定义统一接口,让原始组件和所有装饰器都实现它,装饰器内部持有一个该接口的指针/引用,把调用转发出去,再在前后插入新逻辑。

  • 定义抽象组件接口(如 Component),声明核心操作(如 operation()
  • 原始类(ConcreteComponent)直接实现该接口
  • 抽象装饰器(Decorator)也继承自 Component,并持有一个 std::unique_ptr 或引用
  • 具体装饰器(如 LoggingDecoratorTimingDecorator)继承 Decorator,重写 operation():先执行附加逻辑(如打日志),再调用被包装对象的 operation()

支持运行时叠加多个装饰器

因为每个装饰器只依赖 Component 接口,不关心被包装的是原始对象还是另一个装饰器,所以可以链式组合:

auto comp = std::make_unique();
auto logged = std::make_unique(std::move(comp));
auto timed = std::make_unique(std::move(logged));
timed->operation(); // 先计时 → 再日志 → 最终执行原始逻辑

这种组合方式完全静态、类型安全,无需反射或宏,也不侵入原有类定义。

用模板 + CRTP 实现零开销装饰(进阶)

若追求极致性能且装饰逻辑固定,可用模板装饰器避免虚函数开销:

  • 定义模板基类 Decorator,其中 T 是被装饰类型
  • 装饰器成员函数直接调用 static_cast(this)->operation()
  • 继承时写 class LoggingDecorator : public Decorator(CRTP)

这种方式编译期绑定,无虚表、无动态分配,但灵活性略低于接口方案,适合已知装饰组合的场景。

注意生命周期与所有权管理

装饰器持有被包装对象,必须明确谁负责释放内存:

  • 推荐用 std::unique_ptr 表达独占所有权,构造时移动传入
  • 若需共享底层对象,可用 std::shared_ptr,但要注意循环引用风险
  • 避免裸指针,除非能保证被包装对象生命周期严格长于装饰器

不复杂但容易忽略。