1、pimpl机制的作用
pimpl(private implementation)其主要作用是解开类的使用接口和实现的耦合。
关于耦合性:耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。
模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差。
软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合。
2、pimpl机制的实现思想
pimpl(private implementation)的思想是将私有的数据和函数放入一个单独的类中,并保存在一个实现文件中,然后在使用它的头文件中对这个类进行前置类型声明,并定义一个指向该实现类的指针。类的构造函数分配这个pimpl类,而析构函数则释放它。这样可以解开类的使用接口和实现的耦合。
3、实际项目开发遇到的问题
有时候需要修改某个类的实现,而这样的修改会导致所有包含该类的代码需要重新编译,当这个类在项目中被大量使用时,那么编译时间是很长的;别人使用“不合法的方法”调用了我们编写的接口,突破了我们期望给予的限制。
使用pimpl能给我们带来的好处:
1.将具体类的实现封装到另一个类中,使用者只能看到一个前置类型声明和对应的指针。除非使用者去修改对应的实现,否则,他将无法知道具体的实现,也就无法通过一些非法的方式去访问。从一定程度上防止了封装的泄漏;
2.同时类与数据成员之间的耦合最低,类看起来总是一个样子,包含该类声明的文件也不会因为类实现的改变而重新编译,节约编译时间。
4、案例:暴露私有实现
cmessage.h:
#ifndef __MESSAGE_H__
#define __MESSAGE_H__
#include <string>
class CMessage
{
public:
CMessage(std::string strWarn);
~CMessage();
void printf();
private:
std::string m_strWarnMsg;
};
#endif //#ifndef __MESSAGE_H__
这是一个消息类,提供消息打印方法。
但是当我们提供该头文件的时候,其实已经暴露了私有的实现。
5、案例:修改实现,导致大量源文件重新编译
cmessage.h:
#ifndef __MESSAGE_H__
#define __MESSAGE_H__
#include <string>
class CMessage
{
public:
CMessage(std::string strWarn);
~CMessage();
void printf();
private:
std::string m_strWarnMsg;
std::string m_strAdditionMsg;//新功能
};
虽然之前的接口没有改变,但是项目中使用到该头文件的所有源码文件都需要重新编译。
6、案例:使用pimpl思想重新设计模块
使用上面提到的pimpl(private implementation)的思想。我们重新设计该类的实现。
cmessage.h:
#ifndef __MESSAGE_H__
#define __MESSAGE_H__
#include <string>
class CMessagePrivate;//实现类前置类型声明
class CMessage
{
public:
CMessage(std::string strWarn);
~CMessage();
void printf();
private:
CMessagePrivate * const d;//实现类指针
};
#endif //#ifndef __MESSAGE_H__
cmessage.cpp:
#include "cmessage.h"
#include "cmessage_p.h"
CMessage::CMessage(std::string strWarn):d(new CMessagePrivate(strWarn))
{
}
CMessage::~CMessage()
{
delete d;
}
void CMessage::printf()
{
d->printf();
}
cmessage_p.h:
#ifndef __MESSAGE_P_H__
#define __MESSAGE_P_H__
#include <string>
class CMessagePrivate
{
public:
CMessagePrivate(std::string strWarn);
~CMessagePrivate();
void printf();
private:
std::string m_strWarnMsg;
std::string m_strAdditionMsg;
int m_fileNo;
};
#endif //#ifndef __MESSAGE_P_H__
cmessage_p.cpp:
#include "cmessage_p.h"
#include <iostream>
CMessagePrivate::CMessagePrivate(std::string strWarn):m_strWarnMsg(strWarn),m_strAdditionMsg(__FILE__),m_fileNo(__LINE__)
{
}
CMessagePrivate::~CMessagePrivate()
{
}
void CMessagePrivate::printf()
{
std::cout << m_strWarnMsg << std::endl;
std::cout << m_strAdditionMsg << ":" << m_fileNo << std::endl;
}
此时,CMessage类还是提供同样的接口printf,只是该接口的实现放到了CMessagePrivate类中实现了,对于使用者毫不知情。
还有,在CMessagePrivate类中任何新加的功能,都只会影响到CMessagePrivate.cpp和CMessage.cpp,任何包含cmessage.h文件的源文件都不需要重新编译,大大节省了项目编译的时间。