个人专著《C++元编程与通用设计模式实现》由清华大学出版社出版。该书内容源于工业级项目实践,出版后市场反馈积极(已加印)。其专业价值获得了图书馆系统的广泛认可:不仅被中国国家图书馆作为流通与保存本收藏,还被近半数省级公共图书馆及清华大学、浙江大学等超过35所高校图书馆收录为馆藏。
深入解析桥接模式:一个C++模板实现的通用桥接类库
1. 桥接模式简介
在软件开发中,我们常常面临这样的困境:一个类可能因为多个维度的变化而变得臃肿不堪。比如一个图形类,既需要支持不同的绘制方式(矢量图、位图),又需要支持不同的形状(圆形、矩形)。如果为每种组合都创建一个子类,类的数量会爆炸式增长。桥接模式(Bridge Pattern) 就是为了解决这个问题而生——它将抽象部分与实现部分分离,使它们可以独立变化。就像是一座桥,连接着抽象和实现,两边可以各自发展而互不影响。
今天我们要介绍的是一个基于C++模板实现的通用桥接类库。它通过模板参数将接口和实现解耦,利用智能指针管理实现对象,并提供简洁的访问方式。无论你是设计图形系统、通信协议,还是游戏角色,这个桥接类都能帮你轻松应对多维度的变化。
2. 代码设计解析
2.1 整体设计思路
这个桥接类的核心思想很简单:用一个模板类桥接接口和实现。它内部持有实现类的智能指针,并通过运算符重载提供对实现的访问。客户端通过桥接对象调用实现的功能,而桥接本身不关心具体实现细节。它的设计亮点在于:
- 类型安全:通过静态断言确保实现类派生自接口,编译期就能发现问题。
- 灵活构造:支持完美转发,可以用任意参数构造实现对象。
- 内存安全:使用std::shared_ptr管理实现对象,自动处理生命周期。
- 便捷访问:重载了->和*运算符,使用起来就像操作指针一样自然。
- 工厂方法:提供静态create方法,支持后处理回调,方便扩展。
2.2 模板参数与类型萃取
template< typename itfcType , typename implType >
class bridge
{
public:
using itfc_t = typename std::remove_pointer< typename std::decay< itfcType >::type >::type;
using impl_t = typename std::remove_pointer< typename std::decay< implType >::type >::type;
static_assert( std::is_base_of<itfc_t , impl_t >::value, "Interface type must be base class of implemention class." );
// …
};
这里做了两件重要的事:
- 类型萃取:通过std::decay和std::remove_pointer,无论用户传入的是Interface、Interface*还是Interface&,内部统一变成裸类型itfc_t。这样处理简化了后续的使用,用户不用操心指针或引用的修饰。
- 静态断言:确保impl_t确实是itfc_t的派生类。如果类型不匹配,编译就会失败,并给出清晰的错误信息。这是C++模板编程中常用的“约束”技巧。
2.3 内部存储与构造
private:
std::shared_ptr< impl_t > pt_imp__;
public:
bridge(){}
template< typename ...Args >
bridge(Args&&... args ){
pt_imp__ = std::make_shared< impl_t >( std::forward< Args >(args)...);
}
- 智能指针管理:使用std::shared_ptr持有实现对象,自动管理内存,避免手动new/delete的麻烦和风险。
- 完美转发构造:构造函数支持可变参数模板,通过std::forward完美转发参数给实现类的构造函数。这意味着你可以用任何方式构造实现对象,包括移动语义。
2.4 静态工厂方法
template< typename ...Args >
static bridge * create( Args&&... args ) {
return new bridge( std::forward<Args>(args)... );
}
template< typename ...Args >
static bridge * create( std::function< void ( bridge * b , Args&&...) > func, Args&&... args ) {
bridge * ret = new bridge( std::forward<Args>(args)... );
if( func ) {
func( ret , std::forward<Args>(args)... );
}
return ret;
}
提供了两个版本的静态工厂方法:
- 基础版:直接构造桥接对象并返回指针。注意这里返回原始指针,调用者需要负责管理生命周期(可以配合智能指针使用)。
- 增强版:接受一个回调函数,在构造完成后立即调用该回调,并将桥接对象指针和构造参数传入。这为后续初始化或注册提供了便利。例如,你可以在回调中将新创建的桥接对象加入某个管理器。
回调函数的签名是void (bridge*, Args&&…),可以灵活地处理各种后置操作。
2.5 访问运算符重载
impl_t * operator->() {
if( pt_imp__ == nullptr ) throw std::runtime_error( "object empty" );
return pt_imp__.get();
}
impl_t& operator*() {
if( pt_imp__ == nullptr ) throw std::runtime_error( "object empty" );
return *pt_imp__.get();
}
这两个运算符的重载让桥接对象用起来就像是指针一样自然:
- bridge->method() 直接调用实现类的方法。
- (*bridge).method() 同样可以。
当内部指针为空时,会抛出std::runtime_error异常,提醒用户对象未初始化。这种设计避免了空指针访问导致的未定义行为。
3. 使用示例
假设我们有一个绘图系统,定义了绘制接口DrawAPI,并有两种实现:矢量绘制VectorDraw和光栅绘制RasterDraw。我们希望图形的抽象(比如形状)与具体的绘制方式解耦,让它们可以独立变化。桥接模式正好派上用场。
3.1 定义接口和实现类
#include <iostream>
#include <memory>
#include <functional>
// 绘制接口
class DrawAPI {
public:
virtual void drawCircle(int x, int y, int radius) = 0;
virtual ~DrawAPI() {}
};
// 矢量绘制实现
class VectorDraw : public DrawAPI {
public:
void drawCircle(int x, int y, int radius) override {
std::cout << "使用矢量方式绘制圆形,圆心(" << x << "," << y << "),半径" << radius << std::endl;
}
};
// 光栅绘制实现
class RasterDraw : public DrawAPI {
public:
void drawCircle(int x, int y, int radius) override {
std::cout << "使用光栅方式绘制圆形,圆心(" << x << "," << y << "),半径" << radius << std::endl;
}
};
3.2 使用桥接类
#include "bridge.hpp" // 假设桥接类头文件为 bridge.hpp
int main() {
// 创建矢量绘制桥接对象
wheels::dm::bridge<DrawAPI, VectorDraw> vectorBridge;
// 注意:这里需要先构造实现对象,可以用构造函数
// 但我们的桥接类要求构造函数中传入实现对象的构造参数
// 假设VectorDraw有默认构造函数,可以这样:
wheels::dm::bridge<DrawAPI, VectorDraw> vectorBridge2; // 使用默认构造函数?实际上桥接类有默认构造,但不会构造实现对象,需要小心。
// 更好的方式是使用工厂方法,在构造时传入参数
auto* pBridge = wheels::dm::bridge<DrawAPI, VectorDraw>::create();
pBridge->drawCircle(10, 20, 5); // 调用实现类的方法
// 使用后记得释放(实际项目中建议用智能指针管理)
delete pBridge;
// 或者使用回调版工厂,进行一些后处理
auto* pBridge2 = wheels::dm::bridge<DrawAPI, RasterDraw>::create(
[](wheels::dm::bridge<DrawAPI, RasterDraw>* b, int x, int y, int r) {
std::cout << "桥接对象已创建,参数: " << x << "," << y << "," << r << std::endl;
},
100, 200, 15 // 假设RasterDraw构造函数需要这些参数
);
pBridge2->drawCircle(100, 200, 15);
delete pBridge2;
return 0;
}
假设我们修改一下,让实现类需要构造参数。比如VectorDraw需要设备上下文句柄,RasterDraw需要像素格式。这样在构造桥接时传入即可。代码可以更灵活。
3.3 更完整的示例
// 假设实现类有参构造
class VectorDraw : public DrawAPI {
int context_;
public:
VectorDraw(int ctx) : context_(ctx) {}
void drawCircle(int x, int y, int radius) override {
std::cout << "使用矢量方式(上下文" << context_ << ")绘制圆形" << std::endl;
}
};
int main() {
// 使用工厂创建,传入构造参数
auto bridge = wheels::dm::bridge<DrawAPI, VectorDraw>::create(42);
bridge->drawCircle(10, 20, 5);
delete bridge;
// 或者使用智能指针管理
std::unique_ptr<wheels::dm::bridge<DrawAPI, VectorDraw>> up(
wheels::dm::bridge<DrawAPI, VectorDraw>::create(42)
);
up->drawCircle(10, 20, 5);
return 0;
}
可以看到,桥接类的使用非常自然,就像操作一个普通的实现类指针。
4. 类图(Mermaid)
下面用mermaid展示桥接类与接口、实现类的关系:
#mermaid-svg-68Qi2vgX6B7pkhLO{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-68Qi2vgX6B7pkhLO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-68Qi2vgX6B7pkhLO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-68Qi2vgX6B7pkhLO .error-icon{fill:#552222;}#mermaid-svg-68Qi2vgX6B7pkhLO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-68Qi2vgX6B7pkhLO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-68Qi2vgX6B7pkhLO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-68Qi2vgX6B7pkhLO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-68Qi2vgX6B7pkhLO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-68Qi2vgX6B7pkhLO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-68Qi2vgX6B7pkhLO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-68Qi2vgX6B7pkhLO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-68Qi2vgX6B7pkhLO .marker.cross{stroke:#333333;}#mermaid-svg-68Qi2vgX6B7pkhLO svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-68Qi2vgX6B7pkhLO p{margin:0;}#mermaid-svg-68Qi2vgX6B7pkhLO g.classGroup text{fill:#9370DB;stroke:none;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-68Qi2vgX6B7pkhLO g.classGroup text .title{font-weight:bolder;}#mermaid-svg-68Qi2vgX6B7pkhLO .nodeLabel,#mermaid-svg-68Qi2vgX6B7pkhLO .edgeLabel{color:#131300;}#mermaid-svg-68Qi2vgX6B7pkhLO .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-68Qi2vgX6B7pkhLO .label text{fill:#131300;}#mermaid-svg-68Qi2vgX6B7pkhLO .labelBkg{background:#ECECFF;}#mermaid-svg-68Qi2vgX6B7pkhLO .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-68Qi2vgX6B7pkhLO .classTitle{font-weight:bolder;}#mermaid-svg-68Qi2vgX6B7pkhLO .node rect,#mermaid-svg-68Qi2vgX6B7pkhLO .node circle,#mermaid-svg-68Qi2vgX6B7pkhLO .node ellipse,#mermaid-svg-68Qi2vgX6B7pkhLO .node polygon,#mermaid-svg-68Qi2vgX6B7pkhLO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-68Qi2vgX6B7pkhLO .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-68Qi2vgX6B7pkhLO g.clickable{cursor:pointer;}#mermaid-svg-68Qi2vgX6B7pkhLO g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-68Qi2vgX6B7pkhLO g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-68Qi2vgX6B7pkhLO .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-68Qi2vgX6B7pkhLO .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-68Qi2vgX6B7pkhLO .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-68Qi2vgX6B7pkhLO .dashed-line{stroke-dasharray:3;}#mermaid-svg-68Qi2vgX6B7pkhLO .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-68Qi2vgX6B7pkhLO #compositionStart,#mermaid-svg-68Qi2vgX6B7pkhLO .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-68Qi2vgX6B7pkhLO #compositionEnd,#mermaid-svg-68Qi2vgX6B7pkhLO .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-68Qi2vgX6B7pkhLO #dependencyStart,#mermaid-svg-68Qi2vgX6B7pkhLO .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-68Qi2vgX6B7pkhLO #dependencyStart,#mermaid-svg-68Qi2vgX6B7pkhLO .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-68Qi2vgX6B7pkhLO #extensionStart,#mermaid-svg-68Qi2vgX6B7pkhLO .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-68Qi2vgX6B7pkhLO #extensionEnd,#mermaid-svg-68Qi2vgX6B7pkhLO .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-68Qi2vgX6B7pkhLO #aggregationStart,#mermaid-svg-68Qi2vgX6B7pkhLO .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-68Qi2vgX6B7pkhLO #aggregationEnd,#mermaid-svg-68Qi2vgX6B7pkhLO .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-68Qi2vgX6B7pkhLO #lollipopStart,#mermaid-svg-68Qi2vgX6B7pkhLO .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-68Qi2vgX6B7pkhLO #lollipopEnd,#mermaid-svg-68Qi2vgX6B7pkhLO .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-68Qi2vgX6B7pkhLO .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-68Qi2vgX6B7pkhLO .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-68Qi2vgX6B7pkhLO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-68Qi2vgX6B7pkhLO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-68Qi2vgX6B7pkhLO :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
实现
实现
持有
通过impl_t间接使用
«interface»
DrawAPI
+drawCircle(x, y, radius)
VectorDraw
+drawCircle(x, y, radius)
RasterDraw
+drawCircle(x, y, radius)
bridge
-shared_ptr<impl_t> pt_imp__
+bridge(Args…)
+create(Args…) : static bridge
+create(func, Args…) : static bridge
+operator->() : impl_t
+operator*() : impl_t&
impl_t
说明:bridge类通过模板参数与具体的实现类关联,内部持有实现类的智能指针,对外提供统一的访问方式。它不直接依赖接口,但通过静态断言确保实现类派生自接口,从而间接遵循了接口约定。
5. 调用流程图(Mermaid)
以create工厂方法为例,展示对象的创建和使用流程:
#mermaid-svg-81mmNjpeJgZ9YJbo{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-81mmNjpeJgZ9YJbo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-81mmNjpeJgZ9YJbo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-81mmNjpeJgZ9YJbo .error-icon{fill:#552222;}#mermaid-svg-81mmNjpeJgZ9YJbo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-81mmNjpeJgZ9YJbo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-81mmNjpeJgZ9YJbo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-81mmNjpeJgZ9YJbo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-81mmNjpeJgZ9YJbo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-81mmNjpeJgZ9YJbo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-81mmNjpeJgZ9YJbo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-81mmNjpeJgZ9YJbo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-81mmNjpeJgZ9YJbo .marker.cross{stroke:#333333;}#mermaid-svg-81mmNjpeJgZ9YJbo svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-81mmNjpeJgZ9YJbo p{margin:0;}#mermaid-svg-81mmNjpeJgZ9YJbo .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-81mmNjpeJgZ9YJbo .cluster-label text{fill:#333;}#mermaid-svg-81mmNjpeJgZ9YJbo .cluster-label span{color:#333;}#mermaid-svg-81mmNjpeJgZ9YJbo .cluster-label span p{background-color:transparent;}#mermaid-svg-81mmNjpeJgZ9YJbo .label text,#mermaid-svg-81mmNjpeJgZ9YJbo span{fill:#333;color:#333;}#mermaid-svg-81mmNjpeJgZ9YJbo .node rect,#mermaid-svg-81mmNjpeJgZ9YJbo .node circle,#mermaid-svg-81mmNjpeJgZ9YJbo .node ellipse,#mermaid-svg-81mmNjpeJgZ9YJbo .node polygon,#mermaid-svg-81mmNjpeJgZ9YJbo .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-81mmNjpeJgZ9YJbo .rough-node .label text,#mermaid-svg-81mmNjpeJgZ9YJbo .node .label text,#mermaid-svg-81mmNjpeJgZ9YJbo .image-shape .label,#mermaid-svg-81mmNjpeJgZ9YJbo .icon-shape .label{text-anchor:middle;}#mermaid-svg-81mmNjpeJgZ9YJbo .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-81mmNjpeJgZ9YJbo .rough-node .label,#mermaid-svg-81mmNjpeJgZ9YJbo .node .label,#mermaid-svg-81mmNjpeJgZ9YJbo .image-shape .label,#mermaid-svg-81mmNjpeJgZ9YJbo .icon-shape .label{text-align:center;}#mermaid-svg-81mmNjpeJgZ9YJbo .node.clickable{cursor:pointer;}#mermaid-svg-81mmNjpeJgZ9YJbo .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-81mmNjpeJgZ9YJbo .arrowheadPath{fill:#333333;}#mermaid-svg-81mmNjpeJgZ9YJbo .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-81mmNjpeJgZ9YJbo .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-81mmNjpeJgZ9YJbo .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-81mmNjpeJgZ9YJbo .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-81mmNjpeJgZ9YJbo .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-81mmNjpeJgZ9YJbo .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-81mmNjpeJgZ9YJbo .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-81mmNjpeJgZ9YJbo .cluster text{fill:#333;}#mermaid-svg-81mmNjpeJgZ9YJbo .cluster span{color:#333;}#mermaid-svg-81mmNjpeJgZ9YJbo div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-81mmNjpeJgZ9YJbo .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-81mmNjpeJgZ9YJbo rect.text{fill:none;stroke-width:0;}#mermaid-svg-81mmNjpeJgZ9YJbo .icon-shape,#mermaid-svg-81mmNjpeJgZ9YJbo .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-81mmNjpeJgZ9YJbo .icon-shape p,#mermaid-svg-81mmNjpeJgZ9YJbo .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-81mmNjpeJgZ9YJbo .icon-shape rect,#mermaid-svg-81mmNjpeJgZ9YJbo .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-81mmNjpeJgZ9YJbo .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-81mmNjpeJgZ9YJbo .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-81mmNjpeJgZ9YJbo :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
开始: create(args…)
调用bridge构造函数
完美转发args构造impl对象存入shared_ptr
返回bridge*指针
客户端通过->调用实现方法
operator->检查指针非空
返回impl_t*
调用impl_t的具体方法
结束
如果使用带回调的create版本,流程稍有不同:
#mermaid-svg-8jkfJNBGux96ygC6{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8jkfJNBGux96ygC6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8jkfJNBGux96ygC6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8jkfJNBGux96ygC6 .error-icon{fill:#552222;}#mermaid-svg-8jkfJNBGux96ygC6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8jkfJNBGux96ygC6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8jkfJNBGux96ygC6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8jkfJNBGux96ygC6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8jkfJNBGux96ygC6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8jkfJNBGux96ygC6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8jkfJNBGux96ygC6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8jkfJNBGux96ygC6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8jkfJNBGux96ygC6 .marker.cross{stroke:#333333;}#mermaid-svg-8jkfJNBGux96ygC6 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8jkfJNBGux96ygC6 p{margin:0;}#mermaid-svg-8jkfJNBGux96ygC6 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-8jkfJNBGux96ygC6 .cluster-label text{fill:#333;}#mermaid-svg-8jkfJNBGux96ygC6 .cluster-label span{color:#333;}#mermaid-svg-8jkfJNBGux96ygC6 .cluster-label span p{background-color:transparent;}#mermaid-svg-8jkfJNBGux96ygC6 .label text,#mermaid-svg-8jkfJNBGux96ygC6 span{fill:#333;color:#333;}#mermaid-svg-8jkfJNBGux96ygC6 .node rect,#mermaid-svg-8jkfJNBGux96ygC6 .node circle,#mermaid-svg-8jkfJNBGux96ygC6 .node ellipse,#mermaid-svg-8jkfJNBGux96ygC6 .node polygon,#mermaid-svg-8jkfJNBGux96ygC6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8jkfJNBGux96ygC6 .rough-node .label text,#mermaid-svg-8jkfJNBGux96ygC6 .node .label text,#mermaid-svg-8jkfJNBGux96ygC6 .image-shape .label,#mermaid-svg-8jkfJNBGux96ygC6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-8jkfJNBGux96ygC6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8jkfJNBGux96ygC6 .rough-node .label,#mermaid-svg-8jkfJNBGux96ygC6 .node .label,#mermaid-svg-8jkfJNBGux96ygC6 .image-shape .label,#mermaid-svg-8jkfJNBGux96ygC6 .icon-shape .label{text-align:center;}#mermaid-svg-8jkfJNBGux96ygC6 .node.clickable{cursor:pointer;}#mermaid-svg-8jkfJNBGux96ygC6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8jkfJNBGux96ygC6 .arrowheadPath{fill:#333333;}#mermaid-svg-8jkfJNBGux96ygC6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8jkfJNBGux96ygC6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8jkfJNBGux96ygC6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8jkfJNBGux96ygC6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8jkfJNBGux96ygC6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8jkfJNBGux96ygC6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8jkfJNBGux96ygC6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8jkfJNBGux96ygC6 .cluster text{fill:#333;}#mermaid-svg-8jkfJNBGux96ygC6 .cluster span{color:#333;}#mermaid-svg-8jkfJNBGux96ygC6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8jkfJNBGux96ygC6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8jkfJNBGux96ygC6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-8jkfJNBGux96ygC6 .icon-shape,#mermaid-svg-8jkfJNBGux96ygC6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8jkfJNBGux96ygC6 .icon-shape p,#mermaid-svg-8jkfJNBGux96ygC6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8jkfJNBGux96ygC6 .icon-shape rect,#mermaid-svg-8jkfJNBGux96ygC6 .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8jkfJNBGux96ygC6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8jkfJNBGux96ygC6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8jkfJNBGux96ygC6 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
开始: create(func, args…)
调用bridge构造函数
完美转发args构造impl对象存入shared_ptr
调用func(bridge*, args…)
返回bridge*指针
后续使用…
6. 总结
6.1 优点
- 解耦抽象与实现:接口和实现可以独立变化,新增实现类不影响现有抽象。
- 类型安全:编译期静态断言确保实现类正确派生自接口,杜绝类型错误。
- 内存安全:内部使用shared_ptr管理实现对象,避免内存泄漏。
- 使用便捷:重载->和*运算符,让桥接对象用起来就像原始指针一样自然。
- 构造灵活:完美转发支持任意参数的构造函数,工厂方法提供后处理扩展点。
6.2 注意事项
- 工厂方法返回原始指针,客户端需要妥善管理生命周期,建议配合智能指针使用。
- 默认构造的桥接对象内部指针为空,访问前需要确保已初始化(可通过有参构造或工厂方法创建)。
- 异常安全性:当实现类构造函数抛出异常时,桥接对象构造会失败,这是预期的行为。
6.3 适用场景
- 多维度变化:当一个类存在多个独立变化的维度(如形状+绘制方式)时,桥接模式可以避免类爆炸。
- 运行时绑定:需要在运行时切换实现时,桥接模式可以让抽象持有实现的引用,方便更换。
- 分离接口与实现:希望将接口与实现彻底解耦,让它们独立开发和测试。
7. 结语
这个桥接模式模板库虽然小巧,却凝聚了现代C++的诸多精华:模板元编程、完美转发、智能指针、静态断言……它以一种简洁而安全的方式,实现了经典设计模式的现代化封装。希望这篇文章能帮助你理解桥接模式及其C++实现,并在实际项目中灵活运用。
如果你有任何疑问或想法,欢迎留言讨论!
参考资料
- Design Patterns: Elements of Reusable Object-Oriented Software
- C++ Reference: std::shared_ptr
- C++ Reference: static_assert
- C++ Reference: std::decay
- 《C++元编程与通用设计模式实现》



