c++如何实现装饰器模式_c++动态增加对象功能【实战】

3次阅读

装饰器模式在c++中通过组合与接口继承实现动态功能扩展,核心是定义抽象component接口,所有装饰器和具体组件均继承它并持有std::unique_ptr,确保类型统一与安全嵌套。

c++如何实现装饰器模式_c++动态增加对象功能【实战】

装饰器模式在 C++ 里不能像 Python 那样写 @decorator

C++ 没有运行时函数注解机制,所谓“装饰器模式”是设计模式层面的结构复用方案,不是语法糖。它靠组合 + 接口继承实现动态扩展,核心是让新功能和原对象拥有相同接口,且能嵌套包装。

必须定义抽象组件接口,否则无法统一调用

这是最容易漏掉的关键点:没有 Component 抽象基类,Decorator 就没法持有并转发请求。常见错误是直接让装饰器继承具体类,导致类型不兼容、无法多层嵌套。

正确做法:

  • Component 是纯虚接口(至少一个 virtual ~Component() = default;虚函数
  • ConcreteComponent 实现该接口
  • Decorator 也继承 Component,并持有一个 std::unique_ptr<component></component>(或裸指针,但推荐智能指针)
  • 每个具体装饰器(如 LoggingDecoratorTimingDecorator)重写虚函数,在调用被包装对象前后插入逻辑

示例片段:

立即学习C++免费学习笔记(深入)”;

struct Component {     virtual ~Component() = default;     virtual void operation() = 0; }; <p>struct ConcreteComponent : Component { void operation() override { /<em> 原始逻辑 </em>/ } };</p><p>struct Decorator : Component { explicit Decorator(std::unique_ptr<Component> c) : component(std::move(c)) {} void operation() override { component->operation(); } protected: std::unique_ptr<Component> component; };</p><p>struct LoggingDecorator : Decorator { explicit LoggingDecorator(std::unique_ptr<Component> c) : Decorator(std::move(c)) {} void operation() override { std::cout << "log: startn"; Decorator::operation(); std::cout << "log: endn"; } };

装饰器链顺序决定行为执行次序,构造时要注意包裹方向

比如 new LoggingDecorator(new TimingDecorator(new ConcreteComponent)) 表示先计时、再日志;反过来则日志在外层,计时在内层。这直接影响副作用发生的时机和异常传播路径。

常见陷阱:

  • 上对象构造装饰器链,导致析构顺序混乱或悬挂引用
  • 传入裸指针但没管理生命周期,底层对象提前销毁
  • 忘记在装饰器析构函数中释放被包装对象(若用裸指针)

推荐统一用 std::unique_ptr 构造,并确保所有装饰器都遵守移动语义。

性能开销来自虚函数调用和分配,高频场景需谨慎

每层装饰器都引入一次虚函数分发,嵌套深时可能影响内联;每次 new 装饰器也带来堆分配成本。如果只是加一两个简单功能(比如统一加锁),考虑用策略模式或模板策略替代。

可优化点:

  • std::shared_ptr 共享底层对象,避免重复拷贝(但注意线程安全)
  • 对固定组合,写一个聚合装饰器(如 LoggingAndTimingDecorator),减少虚调用层数
  • 编译期装饰可用模板 + CRTP,但失去运行时灵活性

真正需要动态增删功能时,虚函数+堆分配仍是标准解法——只是得清楚代价在哪。

最常被忽略的是:装饰器本身也要参与资源管理。比如某个装饰器打开了文件或申请了缓冲区,它的析构函数必须显式清理,不能只依赖被包装对象的析构。

text=ZqhQzanResources