桥接模式的核心是组合而非继承,通过抽象层持有一个实现接口指针(如std::unique_ptr),实现两者正交变化与运行时解耦。

桥接模式的核心不是继承,而是组合 + 接口指针
桥接模式要解决的问题很直接:当抽象(Abstraction)和实现(Implementation)两个维度都可能变化时,用继承会爆炸式生成子类(比如 WindowsButton + MacButton × ScrollBar + ProgressBar = 4 个类),而桥接把实现抽成独立接口,让抽象持有一个 Implementation* 指针,运行时动态绑定。
关键判断:如果你正在写一个类,发现它既要适配多种平台(Win/Linux/macOS),又要支持多种行为变体(轻量/增强/调试版),且这两者组合频繁——那 Bridge 就比 virtual 继承更松、更易扩展。
常见错误现象:Abstraction 直接继承 Implementation;或把实现类硬编码在抽象内部(new ConcreteImplementation()),导致无法替换。
- 抽象类只定义高层操作(如
draw()、click()),不关心底层怎么画 - 实现接口只声明底层能力(如
drawRect()、drawCircle()),不暴露平台细节 - 抽象类构造时接收
Implementation*,建议用std::unique_ptr管理生命周期(避免裸指针悬空)
C++ 中如何定义桥接的两层接口
抽象层和实现层必须解耦编译:头文件不能互相 #include 实现类,只能前向声明或依赖接口指针。标准做法是把实现接口(Renderer)单独定义为纯虚类,抽象类(Shape)仅包含 std::unique_ptr 成员。
示例片段:
class Renderer {
public:
virtual ~Renderer() = default;
virtual void renderCircle(float radius) = 0;
virtual void renderSquare(float side) = 0;
};
<p>class VectorRenderer : public Renderer {
public:
void renderCircle(float radius) override { /<em> ... </em>/ }
void renderSquare(float side) override { /<em> ... </em>/ }
};</p><p>class RasterRenderer : public Renderer {
public:
void renderCircle(float radius) override { /<em> ... </em>/ }
void renderSquare(float side) override { /<em> ... </em>/ }
};
</p>
注意:Renderer 必须有虚析构函数,否则通过基类指针 delete 会未定义行为;renderCircle 等函数名应反映能力语义,而非平台语义(不要叫 winDrawCircle)。
抽象类如何安全持有并使用实现对象
抽象类(如 Circle)不应知道具体实现类型,只调用 Renderer 接口。构造时传入 std::unique_ptr 是最常用且安全的方式——自动管理内存,且明确所有权。
容易踩的坑:
- 用裸指针(
Renderer*)但忘记外部保证生命周期 —— 抽象对象存活时,实现对象已被 delete - 在抽象类中 new 具体实现(如
new VectorRenderer)—— 彻底破坏桥接意义,无法替换 - 把
Renderer做成模板参数(template)—— 编译期绑定,失去运行时切换能力
正确示例:
class Circle {
std::unique_ptr<renderer> renderer_;
float radius_;
public:
Circle(std::unique_ptr<renderer> r, float rads)
: renderer_(std::move(r)), radius_(rads) {}
<pre class="brush:php;toolbar:false;">void draw() { renderer_->renderCircle(radius_); }
};
为什么不用 PIMPL 而用桥接
PIMPL(Pointer to Implementation)也用指针隐藏实现,但它解决的是编译依赖和 ABI 稳定性问题,属于同一模块内的封装技巧;桥接是设计模式,解决的是两个正交变化维度的解耦,常跨模块甚至跨团队协作。
性能与兼容性影响:
- 桥接引入一次虚函数调用开销(
renderer_->renderCircle()),但现代 CPU 分支预测很好,实际影响微乎其微 - 如果实现类状态很大(如含纹理缓存),桥接允许复用同一个
Renderer实例服务多个抽象对象,节省内存 - Windows 和 Linux 下
std::unique_ptr行为一致,无平台兼容性风险;但要注意Renderer的虚函数表布局在不同编译器间是 ABI 兼容的(只要不开启-fvisibility=hidden之类破坏导出的选项)
真正容易被忽略的是:桥接后,抽象和实现的头文件可以分别发布、版本升级,只要接口不变,使用者无需重新编译抽象层代码——这点在大型项目中比“少写几个类”重要得多。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











