C++ Idioms: Pimpl

C++的Pimpl惯用法或者说Pimpl模式,又被称为编译防火墙,是一种在头文件中隐藏实现的方式。Pimpl很古老,可能在标准C++诞生之前就有了这种用法,其间争论也早已尘埃落定,用和不用各有利弊,主要还是看组织内部的规范和项目的需要。最近Team一直同时在两个subsystem下工作,两个subsystem的code base一个用了Pimpl一个没有用,是以在Team中产生了到底要不要用的争论。虽然SA的决定是维持现状,但还是总结下Pimpl的相关知识,以备参考。

Pimpl 没有固定的形式,有的很复杂,如Qt中的private class和D-Pointer的结构。而Team在项目中用到的相对很简单,只是一个智能指针加一个Inner Class, 基本结构如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Foo.h

class Foo {
public:
Foo();
virtual ~Foo();
private:
class Pimpl;
boost::scoped_ptr<Pimpl> _pimpl;
};

// Foo.cpp
class Foo::Pimpl {
public:
// data or functions
}

Foo::Foo() : _pimpl(new Pimpl) {}

Foo::~Foo() {}

使用这种结构的好处:

  1. 成员变量的修改不会影响类的头文件,避免重新编译所有inclue类头文件的模块
  2. 类的头文件不需要include 成员变量的头文件,减少编译依赖,加快编译速度
  3. 更好的封装类的实现细节

而相应的缺点:

  1. 增加了代码复杂度
  2. 造成代码可读性下降
  3. 由于指针间接调用造成的性能下降

至于要不要使用Pimpl, 要视情况而定。如果你的工程更注重减少依赖,隐藏实现,Pimpl正适合你。相反如果你的工程中的类含有很多虚函数,又会被大量调用,最好考虑下Pimpl会不会带来性能问题。最近就遇到一个例子,一个库和Qt有冲突,在moc class的头文件中inclue这个库的头文件就会有编译问题。最后就是用Pimpl的方法解决了头文件的依赖。