C++程序在运行时有两种方式加载动态连接库:隐式链接和显式链接。
加载动态库文件就是将动态库中的代码逻辑映射到用户进程地址空间,就在程序执行的时候进行函数调用。
隐式链接
隐式链接是在程序开始执行时就将动态库文件加载到应用程序当中,需要在程序构建编译阶段就指定好,这种方式比较常用
unix的动态库只有.so文件,使用时需要配置工程:
- 包含库头文件
- 包含库.so文件地址
- 链接库名称(多种方式)
windows的动态库包括.lib和.dll文件,其中.lib文件包含了导出函数的入口信息,.dll文件包含了导出函数的具体实现,使用时需要配置工程:
- 包含库头文件
- 包含库.lib文件地址
- 链接库名称
- 拷贝.dll文件到程序执行目录
关于通过cmake构建工程隐式链接动态库具体可参看cmake文档
显式链接
显式链接是程序在执行过程中可以随时加载、随时卸载动态库文件,所以显式链接比较灵活,适合用于热更新。
这里通过一个简单的demo来演示如何生成和使用,在动态连接库中显式链接导出的类。
生成
创建项目,生成动态库。
头文件中包含C++抽象类的接口和导出函数,将实际的派生类实现隐藏在动态库中,通过这种方式导出类。
如果只是需要导出C风格的函数,那么头文件中声明函数,实现部分隐藏在动态库中,在每个函数声明成导出即可。
项目结构
.
├── build
├── CMakeLists.txt
└── src
├── mytool.cpp
└── mytool.h
CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
# support c++11
add_definitions(-std=c++11)
project(mytool)
set(MYTOOL_SRC
src/mytool.h
src/mytool.cpp
)
# use SHARED mode as dynamic link
add_library(mytool SHARED ${MYTOOL_SRC})
# if this lib also depend on other lib, should add link here
#target_link_libraries(mytool
# # some lib name
#)
mytool.h
#ifndef _MYTOOL_H
#define _MYTOOL_H
// you can also use a namespace to wrap the code
class MyTool
{
public:
virtual ~MyTool() {}; // shoud be virtual
// interface functions
virtual void fun1() = 0;
virtual int fun2(int x, int y) = 0;
};
// export the implementation, will generate both .lib and .dll if in windows and .so for unix
#ifdef _WIN32
#define MYTOOL_EXPORT extern "C" __declspec (dllexport)
#else
#define MYTOOL_EXPORT extern "C"
#endif
MYTOOL_EXPORT MyTool *CreateMyTool();
#endif // !_MYTOOL_H
mytool.cpp
#include <stdio.h>
#include "mytool.h"
class MyToolExtend : public MyTool
{
public:
MyToolExtend() {}
virtual ~MyToolExtend() {}
virtual void fun1() override;
virtual int fun2(int x, int y) override;
};
void MyToolExtend::fun1()
{
printf("real fun1 called\n");
}
int MyToolExtend::fun2(int x, int y)
{
printf("real fun2 called\n");
return x + y;
}
MyTool *CreateMyTool()
{
return new MyToolExtend();
}
通过cmake编译
linux会生成libmytool.so
windows会生成mytool.lib
和mytool.dll
使用
创建C++程序
main.cpp
#include <iostream>
#include "mytool.h" // include the lib header
// include shared lib load
#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h> # in linux, should also link to dl system library when build
#endif
// define shared lib load handler
typedef MyTool *(*CreateMyToolFunc)();
#ifdef _WIN32
HINSTANCE gDllHandler = nullptr;
const char *gDefaultSharedLibPath = "mytool.dll"; // here put it the same path
#else
void *gSoHandler = nullptr;
const char *gDefaultSharedLibPath = "libmytool.so"; // here put it the same path, some linux must use ./libmytool.so
#endif
int main()
{
#ifdef _WIN32
// load shared lib
gDllHandler = LoadLibrary(gDefaultSharedLibPath);
if (!gDllHandler)
std::cout << "load shared lib failed" << std::endl;
CreateMyToolFunc create_mytool = (CreateMyToolFunc)(GetProcAddress(gDllHandler, "CreateMyTool"));
MyTool *my_tool = create_mytool(); // get the derived class instance from shared lib
my_tool->fun1();
int z = my_tool->fun2(2, 3);
printf("z: %d", z);
// when all done, unload shared lib
FreeLibrary(gDllHandler);
gDllHandler = nullptr;
#else
gSoHandler = dlopen(gDefaultSharedLibPath, RTLD_LAZY);
if (!gSoHandler)
std::cout << "load shared lib failed" << std::endl;
CreateMyToolFunc create_mytool = (CreateMyToolFunc)(dlsym(gSoHandler, "CreateMyTool"));
MyTool *my_tool = create_mytool(); // get the derived class instance from shared lib
my_tool->fun1();
int z = my_tool->fun2(2, 3);
printf("z: %d", z);
// when all done, unload shared lib
dlclose(gSoHandler);
gSoHandler = nullptr;
#endif
return 0;
}
运行结果
real fun1 called
real fun2 called
z: 5
注意:
- linux下使用需要链接系统
dl
库 - linux下指定so文件路径可能要
./
符号 - windows下使用仅需要dll文件