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 idiom是Bridge模式的好例子.
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 总结
很简单, 没有啥好说额.