c++如何实现适配器模式_c++类适配器与对象适配器【进阶】

2次阅读

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

c++如何实现适配器模式_c++类适配器与对象适配器【进阶】

类适配器:用 public 继承复用旧接口

类适配器本质是让新类通过 public 继承已有类,同时实现目标接口。它依赖 c++多重继承能力,适用于被适配类是具体类(而非纯虚类)、且允许继承的场景。

典型错误是误用 privateprotected 继承,导致目标接口无法被外部调用;或忽略被适配类析构函数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 的生命周期完全交给适配器管”,这直接影响异常安全和资源归属。

text=ZqhQzanResources