近期在编写C++接口类时,遇到一个问题。要求是只暴露接口,隐藏实现细节,提供的接口头文件中只提供公共成员函数的声明,且不能包含其他头文件。
比方说现在我们有两个类 dog和cat,均包含eat()成员方法,
//dog类
//dog.h
class Dog
{
public:
void eat(){
print("Dog eat!\r\n");
}
}
//cat类
//cat.h
class Cat
{
public:
void eat(){
print("Cat eat!\r\n");
}
}
还有一个控制器类control,有两个私有猫狗指针,一个公开的Eat()方法,
//Control类
//control.h
#include "dog.h"
#include "cat.h"
class Control
{
private:
Dog* pdog;
Cat* pcat;
public:
void Eat(){
pdog->eat();
pcat->eat
}
}
在主程序main中,我们可以创建Control对象,调用Eat()方法,间接调用猫狗的eat()方法。
#include "control.h"
Control* pmyctr;
int main()
{
pmyctr->Eat();
return 0;
}
看着没什么问题,但是如果把control类作为一个接口,当别人使用这个control接口时就需要包含control.h这个头文件,间接又包含了dog.h和cat.h两个头文件,那会造成什么影响?
举个例子,比如有100个.cpp都引用我这个接口,那么如果我的cat.h中修改代码
//printf("Cat eat!\r\n");
printf("Cat eat an apple.\r\n");
看似毫无影响,但是由于头文件被多次包含,将会导致100个.cpp文件都要重新编译!!!那如果上万个呢,芭比Q了!!
这里就要用到一种设计模式----Pimpl模式,将一个类的所有实现细节都“代理”给另外一个类,而自己只负责提供接口,接口的实现则是通过调用impl类对应的函数来实现。
//PImpl.h
class Control; //前置声明
class PImpl
{
private:
Control* pctr;
public:
PImpl();
virtual ~PImpl();
//与Control中的public一样
void Eat();
}
//PImpl.cpp
#include "control.h"
PImpl::Pimpl()
{
pctr = new Control;
}
PImpl::~Pimpl()
{
if(pctr){
delete pctr;
}
}
void PImpl::Eat()
{
pctr->Eat();
}
这便完成了接口与实现的分离 ,
#include "Pimpl.h"
Pimpl* pmyctr{};
int main()
{
pmyctr->Eat();
return 0;
}