7.1 the Pimpl Idiom

从一个简单的例子看起. 假设要设计一个Person类, 存储一些个人的信息. 除了常见的设计方式之外, 你可能还见过这样的定义:

1
2
3
4
5
6
7
8
9
10
11
struct Person
{
std::string name;
void greet();

Person();
~Person();

class PersonImpl;
PersonImpl* impl;
}

为什么要这么设计?

PersonImpl并不是在头文件中定义的, 而是在.cpp中定义的.

1
2
3
4
5
6
7
8
9
10
struct Person::PersonImpl
{
void greet(Person* p);
}

Person::Person()
: impl( new PersonImpl)
{}

Person::~Person() { delete impl; }

实现greet():

1
2
3
4
5
6
7
8
void Person::greet()
{
impl->greet(this);
}
void Person::PersonImpl::greet(Person* p)
{
printf("hello %s", p->name.c_str());
}

这就是所谓Pimpl idiom.

三个好处:

  • 大部分类实现是被隐藏的. 可以对用户隐藏不需要暴露的信息.

  • 修改隐藏的Impl的数据成员不回损害二进制兼容性

  • 头文件只需要包含声明部分, 不需要包含实现内容. 如果需要在Person中增加一个vector<string>的私有成员, 你需要在Person.h#include <vector><string> (这意味着所有使用person.h的文件都会包含这两个头文件). 而如果将其放在impl中, 则只需要在.cpp中包含这两个文件, 不会影响头文件.

另一个副作用是这样会加快编译速度.

另外, Pimpl idiomBridge模式的好例子.

7.2 Bridge

看一些更通用的例子.

考虑我们有两个类: 一个几何形状类和一个将它们画出来的渲染类.

1
2
3
4
struct Renderer
{
virtual void render_circle(float x, float y, float radius) = 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct VectorRenderer : Render
{
void render_circle(float x, float y, float radius) override
{
cout << "Rasterizing circle of radius " << radius << endl;
}
}

struct RasterRenderer : Render
{
void render_circle(float x, float y, float radius) override
{
cout << "Drawing a vector circle of radius" << radius << endl;
}
}

形状的基类Draw:

1
2
3
4
5
6
7
8
9
struct Shape
{
protected:
Renderer& renderer;
Shape(Renderer& render) : renderer{renderer}{};
public:
virtual void draw() = 0;
virtual void resize(float factor)=0;
}

在这里, Shape中有一个到Renderer的引用. 这就是我们的Bridge要做的.

创建具体的Shape类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct Circle : Shape
{
float x,y,radius;
void draw() override
{
renderer.render_circle(x,y,radius);
}

void resize(float factor) override
{
radius *= factor;
}

Circle(Renderer& renderer, float x, float y, float radius)
: Shape{render}, x{x}, y{y}, radius{radius}
{
}
};

使用 :

1
2
3
4
5
RasterRenderer rr;
Circle raster_circle{ rr, 5, 5, 5};
raster_circle.draw();
raster_circle.resize(2);
raster_circle.draw();

7.3 总结

很简单, 没有啥好说额.