C++学习笔记(十一)——PIMPL设计模式
本篇解决的问是:
- 什么是PIMPL设计模式;
- PIMPL设计模式好与坏;
- 代码示例实现PIMPL设计模式;
什么是PIMPL设计模式
PIMPL设计模式是Pointer to implement(指针实现)的缩写。其设计特点可以解释为如下3点:
1、头文件只做接口声明;
2、用另外一个.cc文件实现接口;
3、调用接口的文件直接包含头文件就可调用相关的接口;
PIMPL设计模式的利弊
将接口声明与实现分开的设计其好处有:
可以实现敏感数据的隐藏
若不愿意让别人看到类内的私有成员变量时,便可以通过PIMPL设计模式来实现。实例代码如下(代码11-1):
/// file@ Student.h
class Student
{
public:
Student();
{...}
Student(const char * name, int num)
{...}
void putName(const char * name)
{...}
void putNum(int num)
{...}
private:
char * _name;
int num;
};
可以看到我们的实现暴露了private成员变量。可以通过PIMPL实现对其隐藏。代码示例如下(代码11-2):
///
/// @file pimpl_test1.h
/// @author XuHuanhuan(1982299154@qq.com)
/// @date 2019-02-23 15:06:04
///
#include <iostream>
using std::cout;
using std::endl;
class Student
{
public:
Student();
Student(const char * name, const unsigned num);
void putName(const char * name);
void putNum(const unsigned num);
void print();
private:
class Secret;
Secret * _psecrt;
};
///
/// @file pimpl_test1.cc
/// @date 2019-02-23 15:11:11
///
#include "pimpl_test1.h"
#include <string.h>
#include <iostream>
using std::cout;
using std::endl;
class Student::Secret
{
public:
char * _pname;
unsigned int _num;
Secret()
{}
Secret(const char * pname, const unsigned int num)
:_pname(new char[strlen(pname)+1]())
,_num(num)
{
strcpy(_pname, pname);
}
};
Student::Student()
{
_psecrt = new Student::Secret();
}
Student::Student(const char * name, const unsigned num)
{
_psecrt = new Student::Secret(name,num);
}
void Student::putName(const char * name)
{
_psecrt->_pname = new char[strlen(name)+1]();
strcpy(_psecrt->_pname,name);
}
void Student::putNum(const unsigned num)
{
_psecrt->_num = num;
}
void Student::print()
{
cout << _psecrt->_pname << endl;
cout << _psecrt->_num << endl;
}
对比(代码11-1)与(代码11-2)的区别可以看出,其区别在于,使用PIMPL的(代码11-2)的私有成员不再是一个确定类型的成员了,而是一个类指针。这样的好处在于屏蔽了具体的私有对象信息,而只是留下了用户需要使用的接口。
节省编译时间
在C++中有头文件(.h)和实现文件(.cpp),一旦头文件发生变化,不管多小的变化,所有引用它的文件都必须重新编译。对于一个很大的项目,C++一次编译可能就会耗费大量的时间,如果代码需要频繁改动,那真的是不能忍。若 利用 PIMPL 将头文件的接口封装到一个实现类中单独编译,就可以减少头文件对编译的依赖,起到减少编译时间的效果。
屏蔽接口具体,防止别人盗取代码
从(代码11-2)可以看出,头文件pimpl_test.h的接口具体实现全部由pimpl_test.cc完成的。所以,若使用方需要使用该接口,则只需要将pimpl_test.h和由pimpl_test.cc编译成pimpl_test.o文件交给使用方即可。而使用方只能看到pimpl_test.h的代码却没有办法看到各个接口的具体实现。从而实现了保护知识产权的作用。
内存开销增加,间接指针多
使用 PIMPL 需要在堆空间上分配和释放内存,内存开销增加,同时也需要更多的间接指针跳转,因此有一些副作用。
代码示例实现PIMPL设计模式
(代码11-2)所示