Pimpl(Pointer to implementation)是针对C++的设计模式Q,它将所有的私有数据成员、私有成员函数隔离到一个.cpp文件中独立实现的类或结构体内。
首先观察一个没有使用Pimpl的例子:
这里有三个文件,foo.h与foo.cpp用于声明CFoo类。main.cpp调用该类,并使用类中的ProcessFile方法。在现实场景中,开发人员在foo.h与foo.cpp实现foo类别的功能,用户通过main函数调用API。
/*File foo.h*/
class CFoo
{
public:
CFoo();
~CFoo();
bool ProcessFile(const CString & csFile);
private:
CFooInternalData m_data;
CHeader m_header;
}
/*File foo.cpp*/
#include "FooInternalData.h"
#include "Header.h"
#include "foo.h"
CFoo::CFoo()
{
}
CFoo::~CFoo()
{
}
bool CFoo::ProcessFile(const CString & csFile)
{
//do something
return true;
}
/*File main.cpp*/
#include "FooInternalData.h"
#include "Header.h"
#include "foo.h"
int main()
{
CFoo foo;
foo.ProcessFile("c:\\data.bin");
return 0;
}
我们希望用户只需要#include "foo.h"即可使用CFoo类的各种功能,但是发现main函数不但要调用foo.h,还需要调用FooInternalData.h与Header.h,因为在CFoo类的具体实现中用到了这两个头文件。这样的设计存在以下问题:
1.头文件的增多会导致编译速度变慢。
2.CFoo类与 CFooInternalData紧紧绑定,只要CFooInternalData发生改变,CFoo类就需要重新编译。
回看刚刚的代码,CFooInternalData和CHeader数据类型需要包含FooInternalData.h与Header.h才能访问的,但是CFoo类中是私有的(private)。换句话说,用户在main函数中自己是用不到这两个数据类型的(因为对他们的数据操作全部封装在了foo.h中)。既然如此,我们希望用户在main函数中也不需要包含这两个头文件。
因此我们就引出了PIMPL设计方法。该方法目的是将所有的私有成员隐藏在.cpp中,这样用户在main函数中就不需要包含相关的头文件了。具体思想是将私有成员(例如CFoo类中private部分的数据和函数)放置在一个类(结构体)中,原始的类通过指针访问封装了私有成员的新结构体。
以下是需要的操作(文章最下面有完整的代码):
首先在foo.h中,只需要声明一个PIMPL类用于编译。public部分和之前没有修改,private私有成员部分,定义了一个自动指针,std::auto_ptr<CFoo_pimpl> m_pImpl,后面将通过调用自动指针来进行原来私有成员需要的操作。
/*File foo.h*/
class CFoo_pimpl; //前置声明
class CFoo
{
public:
CFoo();
~CFoo();
bool ProcessFile(const CString & csFile);
private:
std::auto_ptr<CFoo_pimpl> m_pImpl;
}
在foo.cpp中,给出了上面foo.h中声明的CFoo_pimpl类的结构与定义。可以看出,这里的CFoo_pimpl类就和我们最上面非PIMPL方法的CFoo类一模一样。
同时该文件中也定义了CFoo类的成员,因为功能都是在CFoo_pimpl类中实现的,所以CFoo类只是调用CFoo_pimpl类方法,从而实现了对代码的封装。
/*File foo.cpp*/
#include "FooInternalData.h"
#include "Header.h"
#include "foo.h"
//定义PIMPl类
class CFoo_pimpl()
{
public:
CFoo_pimpl()
{
}
~CFoo_pimpl()
{
}
bool ProcessFile(const CString & csFile)
{
//do something
return true;
}
private:
CFooInternalData m_data;
CHeader m_header;
};
//定义CFoo类中的成员
CFoo::CFoo()
:m_pImpl(new CFoo_pimpl())
{
}
CFoo::~CFoo()
{
//为空即可,std::auto_ptr可以自动析构
}
bool CFoo::ProcessFile(const CString & csFile)//通过调用pImpl类中的方法实现
{
//just call your PIMPL function ;-)
return m_pImpl->ProcessFile(csFile);
}
最后用户在调用foo.h时,就不需要包含CFoo类内部私有成员所需的头文件啦。(*^▽^*)
/File main.cpp*/
#include "foo.h"
int main()
{
CFoo foo;
foo.ProcessFile("c:\\data.bin");
return 0;
}
参考内容: