在规模稍大一些的应用程序开发过程中,通常会将一部分逻辑相对独立的代码封装成类库,或者引用功能相似的外部共享类库,与主程序共同链接生成最终的应用程序。
但是为了方便将不同模块委托给独立的开发团队,并且保证主程序的开发过程不依赖具体的子模块,通常将子模块定义成具有清晰接口的实现体,并以动态库DLL形式封装。这种形态在设计模式上可以参考门面模式(Facade Pattern),也可称为外观模式。模式结构图如下所示:
关于门面模式的知识可以参考下面这位大神的文章,此处不再赘述。
23种设计模式-门面模式(外观模式)_老杨叔叔csdn的博客-CSDN博客_门面模式
以下将介绍在实际工程中提炼出的一个动态加载子系统模块的通用C++模板类,供小伙伴们参考。
//
// Filename: SubSysLoader.hpp
//
#ifndef _SUB_SYSTEM_LOADER_H_
#define _SUB_SYSTEM_LOADER_H_
#include <iostream>
#include <windows.h>
template< typename SS >
class SubSysLoader {
public:
SubSysLoader() : _modPath(""), _creator(0), _release(0) {};
SubSysLoader(const std::string& modPath) :
_modPath(modPath), _creator(0), _release(0) {
loadModel(modPath);
};
virtual ~SubSysLoader() {
if (_hModel != NULL) {
_hModel = NULL;
_creator = nullptr;
_release= nullptr;
return;
}
};
//构造 方法
SS * create() {
if (! _creator) {
return nullptr;
}
typedef SS * (*_FUNC) ();
return ((_FUNC)_creator)();
};
//释放 方法
void release(SS * mod) {
if (!_release) {
return;
}
typedef void(*_FUNC) (SS *);
return ((_FUNC)_release)(mod);
};
inline bool isValid() { return (_hModel && _creator && _release); };
private:
bool loadModel(const std::string& modPath) {
//动态加载DLL
_hModel = LoadLibrary(modPath.c_str());
if ( ! _hModel ) return false;
//获取 DLL 中函数入口
_creator = GetProcAddress(_hModel, "creator");
_release = GetProcAddress(_hModel, "release");
return isValid();
};
private:
std::string _modPath;
HINSTANCE _hModel; //动态库句柄
FARPROC _creator;
FARPROC _release;
}; //[EOF SubSysLoader ]
#endif // _SUB_SYSTEM_LOADER_H_
以下详细说明一下这个通用模板类中的几个关键点:
1. 动态加载动态库的方法:
_hModel = LoadLibrary(modPath.c_str());
if ( ! _hModel ) return false;
以上windows系统上的实现。如果要在linux上运行,可使用 dlopen 方法。
2. 加载动态库中的 构建方法和释放方法:
_creator = GetProcAddress(_hModel, "creator");
_release = GetProcAddress(_hModel, "release");
此处两行代码通过 GetProcAddress 函数从动态库模块中获取 creator 和 release 两个API的入口地址。
3. 包装构建方法和释放方法:
SS * create() //构造
void release(SS * mod) //释放
这两个方法在成功加载动态库之后,利用动态库中的API显式创建模板对象 和 显示析构模板对象。
这里一定要强调一下:为什么动态模块中要提供 release方法,而不是直接在宿主应用中直接用 delete 方法?
理由是子系统模块中在析构对象的时候有可能需要做特殊处理,而这样的特殊处理逻辑在宿主应用中不具备的。特别是当模板类是抽象接口的时候,宿主应用中根本不知道 delete 应该使用哪个析构函数。
4. 使用方法:
有了这个通用C++模板类之后,动态加载子模块,创建子系统对象,销毁该对象就变得容易多了。参考下面的代码:
class SUBSYS_INTF_A;
SubSysLoader<SUBSYS_INTF_A> sslA(std::string(".\\plugin\\modA.dll"));
SUBSYS_INTF_A insA = sslA.create(); //创建对象
......
sslA.release(insA); //释放对象
以上就是本篇的全部内容,希望能帮上小伙伴们。
喜欢的话, 请点个“赞” 吧。