c++可通过虚函数接口与包装类实现装饰器模式;必须用纯虚函数定义component接口以支持运行时多态和多层叠加,装饰器通过std::unique_ptr持有被装饰对象并转发调用,需严格管理所有权避免悬空指针。

C++ 没有原生装饰器语法,但用包装类 + 虚函数接口完全能实现等效效果;关键不是“像 Python 那样写 @decorator”,而是让扩展行为可组合、不侵入原始逻辑。
为什么必须用虚函数定义 Component 接口
装饰器要和被装饰对象“看起来一样”,就得共享同一接口。如果只靠模板或继承具体类,会失去运行时替换能力,也无法叠加多层装饰(比如 LoggingDecorator 包一层,再包一层 RetryDecorator)。
- 基类
Component必须声明纯虚函数(如virtual void execute() = 0),否则派生类无法被统一指针管理 - 不能把接口函数设为
final或非虚——否则ConcreteDecorator无法重写行为 - 若用值语义(非指针/引用)传递,多态失效;必须用
std::unique_ptr<component></component>或Component&持有
装饰器类如何正确转发并增强调用
每个装饰器内部持有一个 std::unique_ptr<component></component>,构造时接收被装饰对象;重写的函数里先执行增强逻辑(如日志、计时),再调用 m_component->execute() —— 这是核心转发逻辑。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 别在装饰器构造函数里直接调用
m_component->execute(),那是初始化阶段,不是使用阶段 - 若需访问被装饰对象的特有接口(非
Component定义的),说明设计已泄漏,应提取共用接口或用 visitor 补充 - 避免裸指针持有:
Decorator析构时自动释放m_component,防止内存泄漏
class LoggingDecorator : public Component {
std::unique_ptr<component> m_component;
public:
explicit LoggingDecorator(std::unique_ptr<component> comp)
: m_component(std::move(comp)) {}
void execute() override {
std::cout execute(); // 关键:转发给下一层
std::cout
<h3>动态组合时容易忽略的生命周期与所有权问题</h3>
<p>装饰器链本质是单向所有权链:<code>OuterDecor → InnerDecor → Concrete</code>。谁 new 谁 delete 不重要,重要的是谁拥有谁。</p>
<ul>
<li>用 <code>std::make_unique</code> 创建最内层对象,然后逐层包裹:
<code>auto ptr = std::make_unique<concretecomponent>();</concretecomponent></code>
<code>ptr = std::make_unique<loggingdecorator>(std::move(ptr));</loggingdecorator></code>
<code>ptr = std::make_unique<timingdecorator>(std::move(ptr));</timingdecorator></code>
</li>
<li>切勿用栈对象地址去构造装饰器(如 <code>ConcreteComponent c; LoggingDecorator d(&c);</code>)—— 装饰器析构后,内部指针悬空</li>
<li>如果需要共享底层对象(比如多个装饰器共用一个 <code>Component</code>),改用 <code>std::shared_ptr</code>,但要注意循环引用风险</li>
</ul>
<h3>不推荐用模板替代虚函数实现“静态装饰器”</h3>
<p>有人尝试用模板参数传入被装饰类型,生成编译期组合(类似 CRTP)。这看似零开销,但实际破坏了装饰器的核心价值:</p>
<ul>
<li>无法运行时决定加哪几层(比如配置文件控制是否启用重试)</li>
<li>每种组合产生独立类型,容器无法统一存放(<code>std::vector<component></component></code> 失效)</li>
<li>错误信息里全是嵌套模板名,调试困难;且编译时间随组合爆炸增长</li>
<li>只有当装饰逻辑极度简单、层数固定、且明确拒绝运行时灵活性时,才考虑模板方案</li>
</ul>
<p>真正难的不是写出来,是想清楚哪部分必须运行时可插拔——那部分就坚决用虚函数;其余可优化的细节(如缓存、内联)再另说。</p></component></component>C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










