一:概述
C++ 的类布局(尤其是私有成员变量)直接影响它的 ABI(应用二进制接口)。如果你在类中添加或修改了私有成员,即使接口不变,编译器生成的二进制布局也会变,从而导致 ABI 不兼容。这意味着使用这个类的代码需要重新编译,严重破坏了库的二进制兼容性。为避免这种情况,Pimpl(Pointer to Implementation) 模式应运而生。
二:Pimpl 模式介绍
Pimpl将类的实际实现细节封装在另一个类中(impl
),对外只暴露一个指向实现的智能指针(如 std::unique_ptr<impl>
)。这样你可以随意修改实现类而不会影响主类的 ABI,因为指针大小在所有平台都是固定的。
三:代码示例
widget.h 为对外公开的接口文件:
class widget {
class impl; // 前向声明实现类
std::unique_ptr<impl> pimpl; // 指向实现的指针
public:
void draw(); // 接口函数,转发到实现
widget(int); // 构造函数:传参数给实现
~widget(); // 析构函数:释放实现
widget(widget&&) noexcept;
widget(const widget&) = delete;
widget& operator=(widget&&) noexcept;
widget& operator=(const widget&) = delete;
};
widget.cpp 为实现细节,隐藏在此文件中:
class widget::impl {
int n; // 实际的成员变量
public:
void draw(const widget& w) { /* ... */ }
impl(int n) : n(n) {}
};
用户视角:
widget w(42); // 实际构造 impl(42)
w.draw(); // 实际转发给 impl->draw()
四:总结
我们可以把这个规则理解为:将实现藏进“黑匣子”,只留遥控器给别人用。