C++开发sdk之PIMPL隐式开发
一、前言
- API的调用者只关注API的接口,并不关注实现API的实现,以及过程变量或函数
- 如果api简单,尽可能只提供一个头文件+一个库给到API调用者,既隐藏隐私,也方便了API调用者
- PIMPL:Pointer to implementation,由指针指向实现,而不过多暴露细节
- 提供API库也需要提供调用API库的示例代码
- C++习惯开发一个类对应一个类声明.h + 一个类实现.cpp
二、一般SDK开发示例
//test_api.h
#ifndef _TEST_API_H
#define _TEST_API_H
#incldue "test.h" //class Test
//可能还会包含其他的类,但用户都用不到的*.h
class TestApi{
public:
TestApi();
~TestApi();
void TestPrint(); //用户需要的API
//其他方法,有些方法可能只是做中转使用,对用户无意义
private:
Test * m_test;
//其他私有成员,对用户无意义
};
#endif //_TEST_API_H
//test_api.cpp
#include test_api.h
#include test.h
//...
TestApi::TestApi()
{
m_test = new Test();
}
~TestApi::TestApi()
{
delete m_test;
}
voide TestApi::TestPrint()
{
m_test->show();
}
//...
//test.h
#ifndef _TEST_H
#define _TEST_H
#include <iostream>
#include <stdio.h>
class Test{
public:
Test(){
}
~TEest(){
}
void show();
//...
private:
//...
};
#endif
//test.cpp
#include "test.h"
void Test::show()
{
printf("i am Test::show()\n");
}
//等等方法的实现
编译动态库libtest_api.so:
g++ -fPIC -c -o test_api.o test_api.cpp
g++ -fPIC -c -o test.o test.cpp
g++ -fPIC -shared -o libtest_api.so test_api.o test.o
//用户调用库示例代码
//user.cpp
#include "test_api.h"
#include <iostream>
#include <stdio.h>
int main()
{
TestApi api = new TestApi();
api->TestPrint();
delete api;
return 0;
}
提供给用户的编译文件需要 testapi/include/test_api.h + testapi/include/test.h + testapi/lib/libtest_api.so + testapi/user.cpp;如果定义的类比较多,按C++习惯提供类头文件也就越多,暴露的对用户无意义的类文件就越多。
用户调用库编译:
g++ -I~/testapi/include -L~/testapi/lib -o user user.cpp -ltest_api
三、PIMPL开发SDK
//test_api.h
#ifndef _TEST_API_H
#define _TEST_API_H
//#incldue "test.h" //不需要添加其他类的头文件
//可能还会包含其他的类,但用户都用不到的*.h
class TestApi{
public:
TestApi();
~TestApi();
void TestPrint(); //用户需要的API
private:
class TestImpl;
TestImpl * m_impl;
};
#endif //_TEST_API_H
//test_api.cpp
#include test_api.h
#include test.h
//其他类头文件
class TestApi::TestImpl
{
public:
TestImpl();
~TestImpl();
void Show();
//其他中转成员函数
public:
Test m_test;// class test
//其他中转类或变量,建议声明为public方便调用~
};
TestApi::TestImpl::TestImpl()
{
m_test = new Test();
}
TestApi::TestImpl::~TestImpl()
{
delete m_test;
}
TestApi::TestApi()
{
m_test = new Test();
}
~TestApi::TestApi()
{
delete m_test;
}
void TestApi::TestImpl::Show()
{
m_test->show();
}
void TestApi::TestPrint()
{
m_impl->show();
//or
//m_impl->m_test->show(); //公有成员
}
//...
其他 test.cpp test.h user.cpp 保持不变,动态库编译方式以及user.cpp编译也一样,提供给用户必要文件就如下:testapi/include/test_api.h + testapi/lib/libtest_api.so + testapi/user.cpp (减少提供了test.h, 如果类多了,类的头文件就少的更多)
四、总结
从示例中,我们可以看到PIMPL式开发SDK有以下优点:
- 降低耦合,头文件稳定,类具体实现改变不影响其他模块的编译,只影响可执行程序的链接
- 接口和实现分离,提高稳定性
- 隐藏了类是声明,只提供对用户的API接口,对模块比较多sdk开发有一定的优势。