CMake动态库
CMake动态库
幻雪前言
相比较于静态库,动态库需要的是得到一些运行时的量,这是和静态库本质的区别
实现动态库
- 确认平台
- 我们配置动态库要注意是在什么平台下,如果是在 win 下需要有静态库作为帮手,把动态库导入
- Windows 和 Linux 在处理静态库和动态库(也称为共享库)时有一些差异,这主要归因于它们的链接和加载机制的不同。这些差异影响了在各自平台上构建和运行程序的方式。
Windows 平台:
静态库(. Lib):
- 在 Windows 上,静态库通常以
.lib
文件形式出现。 - 当程序编译时,静态库的内容被复制到最终的可执行文件(
.exe
)中。 - 因此,部署时不需要静态库文件,因为所有必需的代码都包含在
.exe
文件内。
- 在 Windows 上,静态库通常以
动态链接库(DLL,. Dll):
- 动态链接库(Dynamic Link Libraries, DLLs)是 Windows 上的共享库。
- 程序在运行时动态地链接到这些库,而不是在编译时。
- 这意味着,为了运行程序,必须有相应的
.dll
文件在可访问的路径上,例如程序所在目录或系统目录。
在 Windows 上,有时即使使用动态库,也需要一个小型的静态库(通常称为导入库),它包含了定位和链接到动态库的指令。
Linux 平台:
静态库(. A):
- Linux 上的静态库通常以
.a
文件形式存在。 - 与 Windows 类似,静态库的内容在编译时被包含到最终的可执行文件中。
- 运行程序时不需要静态库文件。
- Linux 上的静态库通常以
共享库(. So):
- Linux 上的共享库通常是
.so
(shared object)文件。 - Linux 上的动态链接处理比 Windows 更灵活。程序可以直接链接到共享库,不需要额外的静态导入库。
- 运行时需要能够访问这些共享库,但不需要编译时的静态库。
- Linux 上的共享库通常是
总结:
- Windows 平台上,静态库(. Lib)通常是必需的,即使是对于动态链接的情况(通过导入库)。而动态库(. Dll)在运行时必须可用。
- Linux 平台上,静态库(. A)用于完全静态链接的情况,共享库(. So)用于动态链接。动态链接在 Linux 上不依赖于编译时的静态库。
这些差异反映了不同操作系统平台的底层架构和设计哲学。
- Win 下如何导入动态库
- 当我们需要 dll 时,在
.cpp
文件的同级 cmakelists 下添加 SHARED 如下 - 就可以生成 dll 文件
- 但是问题在于,构建 exe 的时候会发生损坏
- 原因上面讲过,所以在 win 平台下我们需要加上
__declspec(dllexport)
到.cpp·
文件以生成静态库帮助导入动态库 - 如果没有加上就会出现文件损毁
- 当我们加上后,在 cmakelists 中添加静态位置,再次进行构建就可以出现 exe 可执行文件
- 当我们需要 dll 时,在
Win 下如何优雅地导入动态库
- 为了优雅地引入
__declspec(dllexport)
,需要去通过宏定义的方式去实现#define CMAKE_STUDY_API __declspec(dllimport)
这样我们就可以通添加 CMAKE_STUDY_API 来使得程序更加易读 - 如果在编译 DLL 的时候定义了,那么会被定义为,这意味着随后所有标记了这个宏的类和函数都是要被导出的。
EXPORT``EXPORT``CMAKE_STUDY_API``__declspec(dllexport)
- 如果没有定义,意味着当前你可能在编译使用 DLL 的应用程序而非 DLL 本身。此时会被定义为,标记的类和函数表示从 DLL 中导入。
EXPORT``CMAKE_STUDY_API``__declspec(dllimport)
例如,在 DLL 的项目中你可能会这样写:
1 | // Define EXPORT when compiling the DLL |
然后,在使用这个 DLL 的其他项目中,你不定义,则会变成用于导入的宏:EXPORT``CMAKE_STUDY_API
1 | // No EXPORT defined here, as we're using the DLL |
Win 下如何表示导出导入 dll
在 Windows 操作系统中,有两种特殊的程序文件叫做 DLL(动态链接库)文件:
导出(Export):想象一个工厂,它生产各种零件。这个工厂希望其他工厂或商店能够买到它的零件,所以它需要告诉大家:“这些是我制作的零件,欢迎购买!” 在程序中,如果你制作了一个 DLL,并且你希望其他程序能使用 DLL 里面的功能(函数、变量等),你需要“导出”这些功能。
导入(Import):现在想象你运营着另外一个工厂,你需要购买之前那个工厂的零件来制造你的产品。那么你就需要“导入”那些零件。在程序中,如果你的程序想要使用其他 DLL 文件里的功能,你就需要“导入”这些功能。
在 C++中,为了告诉电脑哪些功能是要被导出的,哪些是要被导入的,我们需要使用一些特殊的关键字。在 Windows 中,我们用 __declspec(dllexport)
来标识要导出的功能,用 __declspec(dllimport)
来标识要导入的功能。
所以当我们结合上面通过宏的=来优化,我们可以写下 export.h
1 |
代码段:
- 当在制作 DLL 文件时,你会在你的代码中定义一个叫
EXPORT
的宏。这个宏只在制作 DLL 文件时使用。 CMAKE_STUDY_API
这个宏将会根据是否定义了EXPORT
来改变它的行为:- 如果定义了
EXPORT
(意味着你正在制作 DLL),CMAKE_STUDY_API
就代表__declspec(dllexport)
,标识功能为“出售”状态。 - 如果没有定义
EXPORT
(意味着你在使用别人的 DLL),CMAKE_STUDY_API
就代表__declspec(dllimport)
,标识功能为“购买”状态。
- 如果定义了
所以,CMAKE_STUDY_API
是一个切换开关,它可以根据你是正在制作还是使用 DLL,来切换为相应的状态。以上都是作为开发者需要知道的,而用户无需知道的,导入导出的作用就是为了运行的时候 dll 可以连接找到我们的函数。
经过以上,我们就可以放把 dll 到 bin 当中,以及头文件, 以提供给用户:
如此也可以生成 exe 文件用于执行
另外需要注意的是,我们生成的 .exe
文件需要去加上 .dll
文件,否则会找不到文件错误
曾经在游玩游戏的时候是否有出现过类似如下,找不到 .dll
事实上需要保证 .dll
在 .exe
同级目录下或者在环境变量下就可以运行
注意事项 :
由于在 win 系统下我们的动态库也是出于不确定的情况,所以需要依赖 lib,而不只是 bin,所以你需要在 .cpp
类文件加入 __declspec(dllexport)
同时还有 cmake 文件要加上 shared