PIMPL惯用法

PIMPL是“Pointer to IMPLementation”的缩写,它是一种编译防火墙——一种防止修改一个头文件会触发许多源文件被重编译的机制———的编程惯用法。
下面举一个例子,假设有一个类BigClass,它有一些内联函数,且和一些其他的类有使用关系(Foo类、Bar类、Baz类等),代码如下:

//bigclass.h
#include "foo.h"
#include "bar.h"
#include "baz.h"

class BigClass{
public:
	BigClass();
	void f1(int a) {...}
	void f2(float f) {...}
	Foo foo;
	Bar bar;
	Baz baz;
};

这样写是有一个问题的,那就是一旦bigclass.h、foo.h、bar.h或是baz.h的任何地方发生了改动,都会导致引用bigclass.h的文件被重编译,如果工程比较大,BigClass被很多文件引用的话,就会导致编译时间边长。
为了解决这个问题,我们可以使用一个过渡类Impl来当一个中间商的角色:

//bigclass.h
class Impl;
class BigClass{
public:
	Bigclass();
	void f1(int a);
	char f2(float f);
	Impl * impl;
}

C++允许声明一个指向未完成类型的指针,在上面的代码中,Impl就是一个未完成类型。它的实现放在cpp文件中:

//bigclass.cpp
#include "foo.h"
#include "bar.h"
#include "baz.h"
#include "bigclass.h"

class Impl{
	void g1(int a);
	char g2(float f);
	Foo foo;
	Bar bar;
	Baz baz;
};

void Impl::g1(int a)
{
	...
}

char Impl::g2(float f)
{
	...
}

Bigclass::Bigclass()
{
	impl = new Impl;
}

void BigClass::f1(int a)
{
	impl->g1(a);
}

通过这样实现,在编译时,针对foo.h、bar.h、baz.h或是对Impl的改动都会重新编译bigclass.cpp,但是bigclass.h不会改变,这就限制了重编译的范围。这样引用bigclass.h的文件也不会再重新编译,减少了编译的时间。
这种方法的缺点是会给程序带来延迟,因为在BigClass的声明与实现之间增加了Impl作为中间层,会增加成员函数调用;再加上现在编译器的编译时间已经比之前大大缩短,因此如果不是一个非常大的类,被非常多的文件引用的话,其实现在也没有必要使用该方法。

原文地址:https://www.cnblogs.com/wickedpriest/p/13729098.html