且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Pimpl常用法

更新时间:2022-08-13 19:33:09

对于规模较大的C++项目,头文件的互包含和互依赖关系经常困扰开发者.例如:
class foo
{
public:
foo();
void method_foo_b(const foo_a& b);
private:
void method_foo_c(const foo_b& c);
private:
foo_b b_;
foo_c c_;
};

int main(...)
{
foo fooi;
}
如果要使得main或者一个必须包含foo的class通过编译,则不仅需要包含foo头文件,还必须包含foo_b, foo_c的头文件.而实际上foo_b, foo_c是foo内部使用的类,完全不需要外部调用着关心.而且这样做带来两个问题:
1. 大量的依赖包含或者互包含带来更多的编译错误和更长的编译时间,对于大项目,其编译时间可能让人无法承受.
2. 实际上暴露了部分内部实现,违背OO封装的原则.

有些人通过继承纯续接口类的方法解决,但对于实际上并不存在多态概念的类而言,使用续基类来封装接口完全是多余的,增加不必要的虚函数开销和维护开销.
Pimpl用了只有一个实现的Bridge模式解决了这一问题.
class foo
{
public:
foo();
void method_foo_b(const foo_a& b);
private:
struct Pimpl;
boost::scoped_ptr pimpl_ptr_;
};
只出现公有接口,自己用的私有函数和成员变量完全被隐藏.
struct foo::Pimpl
{
void method_foo_b(const foo_a& b) {...};
void method_foo_c(const foo_b& c) {...};
}
void foo::method_foo_b(const foo_a& b)
{
pimpl_ptr_->method_foo_b(b);
}

个人经验,使用Pimpl有几个需要注意的地方,
1. 不要滥用,终究还是增加了一个间接的层次,尽量只在模块的边界使用.
2. 对于内部操作比较复杂的类, 应该把操作移到pimpl里执行,对于非常简单的类,可以只在pimpl里存放成员变量.