SDK(动态链接库dll)的封装技巧
一、说明
通过上篇文章,我们知道对于封装API,目的为了代码复用等,其中还有一个重要的原因,就是隐藏实现。
说到隐藏实现,在封装C++的SDK库(动态dll库)时,可以使用Pimpl的方式来实现。
二、Pimpl
Pimpl是“pointer to implementation”的缩写,目的就是避免在头文件中暴露私有细节。
Pimpl是C++ 在构建导出库接口时特有的技术手段。 即是将类中所有私有变量以及私有方法,封装在一单独的实现私有类中。我们在类
中通过一指向私有类的私有指针,访问这些私有数据。而私有类的具体定义和实现,放入cpp文件中。
Qt中的D指针就是Pimpl方式的实现。
我们还以上节的代码来举例。
比如,我想提供几个接口函数,来实现一个计算,通过数据两个参数,我输出一个计算值。如下所示:
#ifndef EXAMPLEDLL_H
#define EXAMPLEDLL_H
#include "exampledll_global.h"
class EXAMPLEDLLSHARED_EXPORT ExampleDLL
{
public:
ExampleDLL();
int calculation(int a, int b);
int add(int a, int b);
int subtract(int a, int b);
int multiply(int a, int b);
int divide(int a, int b);
};
#endif // EXAMPLEDLL_H
这个calculation函数的实现如下:
#include "exampledll.h"
ExampleDLL::ExampleDLL()
{
}
int ExampleDLL::calculation(int a, int b)
{
int x = 0;
x = add(a,b) + subtract(a,b) + multiply(a,b) + divide(a,b);
return ( x);
}
int ExampleDLL::add(int a, int b)
{
return ( a + b );
}
int ExampleDLL::subtract(int a, int b)
{
return ( a - b );
}
int ExampleDLL::multiply(int a, int b)
{
return ( a * b );
}
int ExampleDLL::divide(int a, int b)
{
return ( a / b );
}
计算的结果是add(a,b) + subtract(a,b) + multiply(a,b) + divide(a,b);四个函数的相加得来的。
如果,我不想让别人看到,我这个函数里面使用了add(a,b) 、 subtract(a,b) 、 multiply(a,b) 、 divide(a,b)这几个函数。只给使用者暴露int calculation(int a, int b)函数。那么此时,我们就可以使用pimpl模式来封装我们的类了。
三、Pimpl的封装形式
在头文件中
#ifndef EXAMPLEDLL_H
#define EXAMPLEDLL_H
#include "exampledll_global.h"
class EXAMPLEDLLSHARED_EXPORT ExampleDLL
{
public:
ExampleDLL();
int calculation(int a, int b);
private:
class ContextPointer;
ContextPointer *d_ptr;
};
#endif // EXAMPLEDLL_H
cpp文件:
#include "exampledll.h"
class ExampleDLL::ContextPointer
{
public:
int add(int a, int b);
int subtract(int a, int b);
int multiply(int a, int b);
int divide(int a, int b);
};
ExampleDLL::ExampleDLL()
{
d_ptr = new ContextPointer();
}
int ExampleDLL::calculation(int a, int b)
{
int x = 0;
x = d_ptr->add(a,b) + d_ptr->subtract(a,b) + d_ptr->multiply(a,b) + d_ptr-
>divide(a,b);
return ( x);
}
int ExampleDLL::ContextPointer::add(int a, int b)
{
return ( a + b );
}
int ExampleDLL::ContextPointer::subtract(int a, int b)
{
return ( a - b );
}
int ExampleDLL::ContextPointer::multiply(int a, int b)
{
return ( a * b );
}
int ExampleDLL::ContextPointer::divide(int a, int b)
{
return ( a / b );
}
如上所示,这样我们就把想要隐藏的代码给隐藏了。
四、优缺点
优点:
1、数据隐藏,非常适合隐藏private实现,如果想要在头文件中暴露public接口,但又不想暴露private实现的细节,使得头文件很干净,可以直接作为API参考。
2、pimpl又被称为编译防火墙。是一种缩短编译时间,降低程序编译依赖的方法。因为,如果头文件里的内容变了,意味着引用该头文件的代码都要被重新编译。如果把这些变动的内容放到只被引用编译一次的源文件中,就不会耗费太多的编译时间。
3、是程序接口有着稳定的ABI(应用程序二进制接口),即不会打破二进制兼容。
缺点:
1、由于数据的隐藏方式,代码的可读性会下降。
2、可能会降低程序的性能,比如调用的函数为虚函数的时候。
注:二进制兼容:在升级库文件的时候,不必重新编译使用此库的可执行文件或其他库文件,并且程序的功能不被破坏,能正常运行。即如果exe调用dll库时,dll库更新了,exe不用重新编译也能正常运行。
上一篇:Qt创建SDK库(dll动态库)并调用SDK库(dll动态库)
下一篇:
本文原创作者:冯一川(ifeng12358@163.com),未经作者授权同意,请勿转载。