编译生成 C++ 库文件

👁️ 1566 ❤️ 487
编译生成 C++ 库文件

目录libdll创建动态库链接动态库动态库中封装类aso

我们介绍 Windows 和 Ubuntu 下生成和使用库文件的操作。

lib

lib 是 Windows 下的静态库,它具有以下特点:

运行不存在

静态库源码被链接到调用程序中

目标程序的归档

静态库是将代码嵌入到使用程序中,多个程序使用时会有多份代码,所以代码体积会增大。动态库的代码只需要存在一份,其它程序通过函数地址使用,所以代码体积小。静态库发生变化后,新的代码需要重新链接嵌入到执行程序中。

要创建静态库,首先在 VS 中建立库项目,这样会产生一些默认框架,如果不想要的话可以先生成控制台项目,然后设置“项目属性-配置类型”为静态库即可;然后直接添加源代码和头文件,右击项目选择“生成”即可产生 .lib 文件

生成静态库后,将 .lib 文件和头文件复制到指定文件目录下导入

#include "../lib/head.h" // 导入静态库头文件

#pragma comment(lib, "../lib/clib.lib") // 使用 pragma 关键字设置库路径

需要注意,如果使用项目创建静态库文件,之后新建项目测试库文件时,要将新建的项目设为启动项目,否则程序会将库文件错认为可执行文件,导致报错

有时我们需要使用 C 语言的静态库,但由于 C++ 编译器会对函数进行“换名”,而 C 编译器则不会,则当调用 C 语言静态库时会导致函数名无法识别,这时就需要使用 extern 声明

extern "C" int add(int, int); // 以 C 编译器方式编译函数声明

#pragma comment(lib, "../lib/clib.lib") // 使用 pragma 关键字设置库路径

dll

dll 是 Windows 下的动态库,它具有以下特点:

运行时独立存在

源码不会链接到执行程序

使用时加载(使用动态库必须使动态库执行)

动态库变化后,如果库中函数的定义(或地址)未变化,其它使用 DLL 的程序不需要重新链接。

动态库数据存放

DLL 文件中存放函数名及其相对于文件首地址的相对地址,还有函数源码信息

LIB 文件中存放函数名及其编号,还有 DLL 文件名

需要注意:使用动态库需要有 .dll 文件和对应的 .lib 文件,其中 .lib 不是静态库。

创建动态库

创建动态库项目

添加库程序

导出库程序:提供给库使用者函数信息

声明导出:使用 _declspec(dllexport) 导出函数地址

注意:动态库编译链接后,也会生成 LIB 文件作为动态库函数映射使用,通过 LIB 文件可以链接到动态库

模块定义文件 .def 导出不改名的函数

//声明导出动态库中的函数

//库文件

_declspec(dllexport) int add(int a, int b)

{

return a + b;

}

//声明导出会导出改名的函数

//模块定义文件导出函数 .def

LIBRARY dll_name //导出的库名

EXPORTS //库导出表

func_name1 @1 //导出的函数1

func_name2 @2 //导出的函数2

func_name3 @3 //导出的函数3

...

//通过这种方式导出函数,能够得到不改名的函数

链接动态库

隐式链接:操作系统负责使动态库执行

头文件和函数原型:在函数原型声明前增加 _declspec(dllimport) ;如果声明在头文件中,需要使用 DLLCLASS_EXPORTS 宏方法

导入动态库的 LIB 文件

在程序中使用函数

dll 存放路径:

与执行文件同目录(推荐)

当前工作目录

Windows 目录

Windows/System32 目录

Windows/System

环境变量 PATH 指定目录

// 导入动态库中的函数

_declspec(dllimport) int add(int, int);

// 通知链接器到 .lib 文件中获取函数编号和 .dll 文件名

#pragma comment(lib, "../lib/clib.lib")

显式链接:程序员负责使动态库执行

定义函数指针类型 typedef

加载动态库 LoadLibrary

获取函数地址 GetProcAddress

使用函数

卸载动态库 FreeLibrary

我们需要通过以下函数实现

// 返回 DLL 实例句柄

HMODULE LoadLibrary(

LPCTSTR lpFileName // 动态库文件名或全路径

);

// 获取函数地址

FARPROC GetProcAddress(

HMODULE hModule, // DLL 句柄

LPCSTR lpProcName // 函数名

);

//卸载动态库

BOOL FreeLibrary(

HMODULE hModule, // DLL 句柄

);

最后给出显式链接动态库的示例:

//定义函数指针

typedef int (*ADD)(int, int);

int main()

{

HINSTANCE hDll = LoadLibrary("CPPdll.dll"); // 加载动态库

ADD myAdd = (ADD)GetProcAddress(hDll, "add"); // 这里使用模块文件导出方法,才能使用不改名的函数名为参数

int sum = myAdd(5, 4);

cout << "sum = " << sum << endl;

FreeLibrary(hDll); // 卸载动态库

return 0;

}

动态库中封装类

在类名前增加 _declspec(dllexport) 定义,导出类成员函数的相对地址,通常使用预编译开关切换类的导入导出定义,例如

#ifdef DLLCLASS_EXPORTS

#define EXT_CLASS _declspec(dllexport)

#else

#define EXT_CLASS _declspec(dllimport)

#endif

如果定义了这一宏,则定义导出,否则定义导入。通过这种方式,程序员在库文件中定义 DLLCLASS_EXPORTS 宏,就可以使用导出类;用户只需要包含头文件,就可以使用导入类

// 导入/导出类

class EXT_CLASS CMath

{

//...

int sum(int a, int b);

int sub(int a, int b);

};

a

a 文件是 Ubuntu 下的静态库,其在主函数编译时就将库导入

# 生成 .o 文件

g++ swap.cpp -Iinclude -c

# 生成静态库 libSwap.a

ar rs libswap.a swap.o

# 链接静态库生成 main

g++ -o main.cpp -Lsrc -lswap main

# 运行main

./main

注意生成的静态库要有 lib 前缀,使用时去掉 lib 前缀

我们也可以生成 Windows 下的静态库文件

ar rs swap.lib swap.o

so

so 文件是 Ubuntu 下的动态库,它不会直接编译进主函数,需要在调用时添加。其中使用了 -fPIC 参数,这在 LAPACK 编译过程中提到,它表示以相对地址来实现代码,从而使其可以加载到任意位置

# 生成动态库 swap.so

g++ swap.cpp -Iinclude -fPIC -shared -o libswap.so

# 以上指令等价于

# gcc swap.cpp -Iinclude -c -fPIC

# gcc -shared -o libswap.so swap.o

# 链接动态库生成main

g++ -o main.cpp -Lsrc -lswap main

# 运行 main,要将动态库所在路径添加到参数

LD_LIBRARY_PATH=src ./main

同样可以生成 Windows 下的动态库文件

gcc -shared -o swap.dll swap.c

← 带浠字的好听网名女(整理72个) 买了台THINKPAD NEO14工程机 →