桥接模式的核心是组合加虚函数,通过abstraction持有implementor指针并多态调用,而非继承;implementor须为抽象基类,具体实现仅继承它;指针类型由生命周期归属决定,避免shared_ptr;接口需稳定,新增功能优先用默认实现。

桥接模式的核心不是继承,是组合加虚函数
桥接模式在 C++ 里最容易被写成“用继承模拟组合”——比如让 Abstraction 继承 Implementor,或者反过来。这完全违背桥接本意。它要解决的是抽象层(如 Shape)和实现层(如 Renderer)各自独立变化的问题,靠的是「持有指针 + 多态调用」,不是类层级的耦合。
关键判断:只要你在 Abstraction 里看到 public Implementor 或者 class Abstraction : public Implementor,就错了。
-
Abstraction类里必须包含一个指向Implementor的指针(通常是std::unique_ptr<implementor></implementor>或裸指针,视生命周期而定) -
Implementor必须是抽象基类,定义纯虚函数(如virtual void draw() = 0) - 所有具体实现(
OpenGLRenderer、SkiaRenderer)只继承Implementor,不碰Abstraction体系 - 构造
Abstraction子类时,必须传入一个具体的Implementor实例,不能在内部 new
std::unique_ptr 还是 raw pointer?看谁管生命周期
桥接对象的生命周期归属决定指针类型——这不是风格选择,是责任划分问题。常见错误是 Abstraction 拿着裸指针却擅自 delete,或用 std::shared_ptr 引入不必要的循环引用。
- 如果
Abstraction完全拥有Implementor(比如用户传进来一个临时对象,你希望它活到Abstraction销毁),用std::unique_ptr<implementor></implementor> - 如果
Implementor是全局单例或由外部长期管理(如 GUI 框架提供的Canvas),用裸指针 + 注释说明“non-owning”,且禁止在析构中 delete - 避免
std::shared_ptr:桥接不涉及共享所有权,加 refcount 只会模糊职责,还可能因循环引用导致泄漏
示例:Circle 构造时接收 std::unique_ptr<renderer>&&</renderer>,移动语义保证所有权清晰;而 UIWidget 接收 Renderer* 并标注 // owned by AppContext。
立即学习“C++免费学习笔记(深入)”;
虚函数表开销小,但别在 hot path 上间接调用
桥接必然引入一次虚函数调用(renderer->draw()),在图形渲染等高频路径上,这比直接内联调用多 1–2 个周期。不是性能瓶颈,但容易被忽略。
- 不要在每像素循环里调用
implementor->put_pixel();应把整块数据交给Implementor批处理(如render_buffer(const uint8_t*, size_t)) - 如果某类
Implementor确实需要极致性能(如 SIMD 渲染),可提供非虚的模板接口(template<typename t> void fast_draw(T&&)</typename>),但桥接主干仍走虚函数,保持扩展性 - 编译器通常能对单实现场景做 devirtualization,但别依赖——确保关键路径不卡在虚调用上
容易漏掉的:Implementor 接口变更会破坏所有子类
桥接解耦的是类结构,不是接口契约。Implementor 基类一旦增加新纯虚函数,所有派生类都得改,否则链接失败。这点比继承更隐蔽,因为改动发生在“底层”。
- 把
Implementor接口设计得足够稳定:优先用组合已有接口(如Drawable+Resizable),而不是堆大而全的基类 - 新增能力尽量用默认实现(C++11 起支持
virtual void save_to_file() { throw std::runtime_error("not implemented"); }),而非强制重写 - 测试时别只测
Abstraction行为,要 mock 不同Implementor的行为差异,尤其验证异常路径下Abstraction是否正确转发错误
真正麻烦的从来不是写对桥接结构,而是当 Renderer 需要支持 Vulkan 后端时,你发现 OpenGLRenderer 和 SkiaRenderer 都得补 3 个新虚函数——这时候才意识到,接口粒度早该按能力切分,而不是按技术栈。








