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文件