类适配器通过public继承复用旧接口,对象适配器通过组合持有被适配者;前者要求被适配类可继承且非final,后者通用性更强、适合第三方类或需运行时切换场景。

类适配器:用 public 继承复用旧接口
类适配器本质是让新类通过 public 继承已有类,同时实现目标接口。它依赖 C++ 的多重继承能力,适用于被适配类是具体类(而非纯虚类)、且允许继承的场景。
典型错误是误用 private 或 protected 继承,导致目标接口无法被外部调用;或忽略被适配类析构函数非 virtual,引发资源泄漏。
- 被适配类(如
Adaptee)需提供可访问的公有成员函数 - 适配器类必须同时继承
Target(目标接口)和Adaptee,且顺序不重要,但推荐class Adapter : public Target, public Adaptee - 若
Adaptee有非virtual析构函数,禁止通过Target*指针 delete 适配器对象 - 不能适配 final 类或含私有/删除构造函数的类
示例关键片段:
class Adaptee {
public:
void specificRequest() { /* ... */ }
};
<p>class Target {
public:
virtual ~Target() = default;
virtual void request() = 0;
};</p><p>class ClassAdapter : public Target, public Adaptee {
public:
void request() override { specificRequest(); }
};对象适配器:用组合 + 指针/引用持有被适配者
对象适配器更常用,它把被适配对象作为成员变量持有(通常用指针或智能指针),通过转发调用实现接口转换。它不依赖继承,兼容性更强,也天然支持运行时切换被适配实例。
立即学习“C++免费学习笔记(深入)”;
常见坑是裸指针管理不当导致悬空、忘记处理空指针、或在拷贝构造中浅拷贝指针引发双重释放。
- 优先使用
std::unique_ptr<adaptee></adaptee>而非裸指针,避免内存泄漏 - 若需共享被适配对象,用
std::shared_ptr<adaptee></adaptee>,但注意循环引用风险 - 适配器的构造函数应显式接收
Adaptee实例(或其智能指针),不默认构造 - 所有对
Adaptee的调用前,检查指针是否为空(除非能保证必不为空)
示例关键片段:
class ObjectAdapter : public Target {
std::unique_ptr<Adaptee> adaptee_;
public:
explicit ObjectAdapter(std::unique_ptr<Adaptee> a) : adaptee_(std::move(a)) {}
void request() override {
if (adaptee_) adaptee_->specificRequest();
}
};选择依据:看被适配类能否修改、是否允许多重继承
如果被适配类是第三方库中的 final 类、只有私有构造、或你无权修改其定义,类适配器直接不可行——只能选对象适配器。
如果被适配类是自己维护的、允许继承、且接口简单稳定,类适配器开销略小(无指针间接寻址、无动态分配),但会暴露被适配类的全部公有接口,可能破坏封装。
- 类适配器无法适配具有非公有构造函数的对象(如单例内部构造)
- 对象适配器可适配任何可实例化的类型,包括抽象基类指针
- 当需要为同一被适配类提供多种目标接口时,对象适配器更容易复用
Adaptee实例 - 模板化适配器(如
template<typename t> class Adapter : public Target</typename>)几乎只能走对象适配器路线
性能与 ABI 兼容性容易被忽略的点
类适配器生成的对象布局受多重继承影响,可能改变 vtable 布局,若用于跨 DLL/DYLIB 边界传递,存在 ABI 不兼容风险;对象适配器因仅含指针成员,布局更可控,适合二进制接口隔离。
- 类适配器的
sizeof通常等于被适配类,对象适配器至少为指针大小(8 字节 on x64) - 频繁创建销毁适配器时,对象适配器的堆分配开销可能成为瓶颈,此时可考虑对象池或栈上
Adaptee实例 + 引用成员 - 若被适配类有移动语义,对象适配器务必实现移动构造/赋值,否则退化为深拷贝
- 调试时,类适配器的调用栈更扁平,对象适配器多一层转发,部分 profiler 可能将其归为“间接调用”
真正棘手的不是语法怎么写,而是决定“要不要把 Adaptee 的生命周期完全交给适配器管”,这直接影响异常安全和资源归属。









