如何在两个PIMPL类型间交互,同时隐藏底层C数据库API?
针对你遇到的封装问题,社区有几种成熟的方案可以在不暴露底层C API类型的前提下,实现两个PIMPL类的交互,完全不需要给应用层暴露getter:
1. 精准友元声明(最常用的直接方案)
在两个类的头文件中,仅让对方的Impl类成为友元,而非整个类。这样既能让实现层互相访问内部的C API句柄,又不会破坏封装性。
比如在Schema.h中:
// 前置声明 class DatabaseConnection; class Schema { private: class Impl; std::unique_ptr<Impl> pImpl; // 仅允许DatabaseConnection的实现类访问内部Impl friend class DatabaseConnection::Impl; public: // 对外暴露的公共接口... };
然后在DatabaseConnection.cpp的Impl实现里,就能直接访问Schema的内部句柄:
void DatabaseConnection::Impl::createTable(Schema& schema) { db_t* db_handle = this->internal_db; db_tabledef_t* table_def = schema.pImpl->internal_table_def; db_create_table(db_handle, table_def); }
这种方式完全不会在应用层暴露任何C API类型,友元关系仅作用于实现层,头文件里只声明了Impl的存在,没有泄露内部细节。
2. 内部专用私有接口(替代友元的方案)
如果不想使用友元,可以在类中添加仅给实现层用的私有接口,通过这个接口传递所需的C API对象。比如在Schema中添加一个私有成员函数,返回db_tabledef_t*,再让DatabaseConnection::Impl成为该函数的友元。
在Schema.h的私有区域:
class Schema { private: class Impl; std::unique_ptr<Impl> pImpl; // 仅内部实现层可见的接口 db_tabledef_t* getInternalTableDef() const; friend class DatabaseConnection::Impl; public: // 对外公共接口... };
在Schema.cpp中实现该函数:
db_tabledef_t* Schema::getInternalTableDef() const { return pImpl->internal_table_def; }
这种方式更明确地划分了“内部调用接口”和“对外公共接口”,同样不会暴露给应用层。
3. 调整职责划分,将创建逻辑移到Schema
既然创建表依赖Schema的定义,可以把createTable的逻辑放在Schema类中,让Schema主动请求DatabaseConnection的内部句柄。比如在Schema的公共接口中添加createInConnection(DatabaseConnection& conn),在这个函数的实现里,通过友元或内部接口获取Connection的db_t句柄,再调用C API。
这种方式更符合单一职责原则:Schema负责表结构定义,同时负责将自身部署到指定连接中,实现逻辑同样封闭在cpp文件里。
内容的提问来源于stack exchange,提问作者iwarv

