Adapter 应依赖抽象接口而非具体实现,用 std::unique_ptr 持有抽象基类指针,构造函数接收基类引用或智能指针;第三方无接口时需自定义抽象层并封装转换逻辑。

Adapter 类怎么写才不和原接口耦合
直接继承或组合被适配对象时,如果硬编码具体类型,后续换库或升级版本就全得改。关键是要把依赖抽象化,让 Adapter 只认接口,不认实现。
- 用
std::unique_ptr持有抽象基类指针,而不是具体类实例 - 构造函数接收基类引用或智能指针,别写死
new ThirdPartyLogger() - 如果第三方库没提供接口,自己先写个
ILogger抽象类,再让 Adapter 实现它
比如第三方只有 LogMessage(const char*),而你系统要求 log(Level, std::string_view),那就定义 ILogger,Adapter 在内部做字符串拼接和等级映射,不暴露 LogMessage 给上层。
std::function 能不能代替传统 Adapter 类
能,但只适合简单、单函数适配;一旦涉及状态(比如需要缓存连接句柄、重试计数器),std::function 就撑不住了。
- 适配无状态的 C 函数(如
qsort的比较器):用std::function<int int></int>完全够用 - 要封装带资源的类(如
LegacyDatabaseConnection):必须用类封装,std::function无法管理生命周期 - 性能敏感路径慎用:每次调用
std::function有间接跳转开销,而虚函数调用在现代编译器下常能内联
示例:把 int legacy_send(void*, size_t) 包成 std::function<bool std::byte>)></bool> 可行;但若内部要重连、加超时,就得写完整 Adapter 类。
立即学习“C++免费学习笔记(深入)”;
模板 Adapter 和虚函数 Adapter 怎么选
看适配目标是否在编译期确定。模板适合“一次写、多处用”,虚函数适合“运行时决定用哪个旧系统”。
- 第三方库有多个版本(v1/v2/v3),接口相似但不兼容:用模板 +
concept约束,避免重复代码 - 程序启动时读配置决定用 Redis 还是 Memcached:必须用虚函数,因为类型在运行时才知
- 模板 Adapter 编译后体积大,每个实例都生成一份代码;虚函数方案二进制更小,但有多态开销
常见误用:用模板硬塞运行时多态逻辑,结果导致链接错误或 ODR 违反——template<typename t> class DbAdapter</typename> 里不能依赖 T 的虚函数表布局,除非 T 是已知接口类型。
析构顺序搞错导致崩溃怎么办
Adapter 持有旧系统资源,又通过 RAII 自动释放,但若 Adapter 析构早于被适配对象,就会 double-free 或访问野指针。
- 检查持有关系:如果 Adapter 内部
std::unique_ptr<legacything></legacything>,确保 LegacyThing 生命周期长于 Adapter - 避免循环持有:Adapter 持有 LegacyThing,LegacyThing 又回调 Adapter 成员函数 → 改用
weak_ptr断环 - 第三方库要求手动
destroy():Adapter 的析构函数里必须显式调用,不能只靠~LegacyThing()
典型报错:double free or corruption (fasttop) 或 EXC_BAD_ACCESS,十有八九是资源释放顺序没对齐。调试时打日志,确认 LegacyThing 的构造/析构和 Adapter 的时机是否匹配。
最麻烦的不是写不对,而是旧系统文档没说清楚资源所有权归属——得翻头文件、看示例代码,甚至反汇编验证。











