(十七)CMake编写并使用自定义库(Windows)

一、库的分类

在Windows下库可以分为静态和动态两种。动态库按照链接的时机又可分为两类:

  • 隐式链接。使用即加载,有时候又称为静态链接或者加载时动态链接。因为不需要使用函数指针即可使用其中的函数, 编译器帮你默默链接,使用上非常方便;(本文只讲解这一种)
  • 显式链接。按需加载,必须使用加载DLL语句和设置DLL函数的指针,有时候称为动态加载或者运行时加载。

与静态链接库的区别在于,静态链接会将所有的对象代码复制到可执行程序中,动态链接则只会在可执行程序中放入含有查找DLL的信息。显然动态链接的可执行程序体积更小,所以除了在生成动态库的时候还需要生成对应的导出符号帮助应用程序在运行时调用动态库符号。

与应用程序相比,DLL程序只能有一个实例,而应用程序可以有多个;应用程序能够作为进程加载,可以管理诸如堆栈、全局内存、文件句柄和消息队列之类的资源,DLL并不能做这些事情。[1]

动态库的优点:

  • 节省内存,减少交换。多个进程共享DLL,静态库不能共享;(这就是为什么DLL叫共享库的原因:多个进程共享)
  • 节省了磁盘空间和带宽;
  • 维护容易。静态库必须重新编译生成exe;
  • 新功能方便添加。通过显示链接发现一个DLL方式;

二、库的内容

假设我们需要创建一个动态库,其功能有:

  • 打印一个apple的void printApple()函数
  • 一个计算和的方法int add(int,int)

分别在library.cpp和library.h增加以下内容:

//library.cpp
#include <iostream>
#include "library.h"
void hello() {
    std::cout << "Hello, World!" << std::endl;
}

int myadd(int i,int j)
{
    return i+j;
}
//library.h
#ifndef ___LIBRARY_H
#define ___LIBRARY_H
void hello();
int myadd(int,int);
#endif 

三、创建并使用动态库(静态链接形式)

在Linux中,源代码中的所有符号是默认导出的,而在Windows下恰好相反。默认全部导出的好处免去繁琐的宏定义用来表示符号,缺点是体积、封装性变差。__declspec标识一个符号是否为需要输出的符号[2],如果你需要输出所有的符号,可以考虑使用set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON),这样就算你不显式指出所有符号,你也可以生成编译所需的*.lib[3]。

// Example of the dllimport and dllexport class attributes
__declspec( dllimport ) int i;
__declspec( dllexport ) void func();

将需要导入和导出的符号用__declspec(dllimport)修饰即可。使用宏定义可以增加可读性:

#define DllImport   __declspec( dllimport )
#define DllExport   __declspec( dllexport )

修改头文件,修改后的头文件如下:

//library.h
#ifndef LIBRARY_H
#define LIBRARY_H
    #define DLL_IMPORT __declspec(dllimport)
    #define DLL_EXPORT __declspec(dllexport)
#endif 											//处理宏重复包含问题

#ifdef BUILD_DLL								
//根据是否传入宏BUILD_DLL决定是编译库还是使用库
    DLL_EXPORT void hello();
    DLL_EXPORT int myadd(int,int);
#else
    DLL_IMPORT void hello();
    DLL_IMPORT int myadd(int,int);
#endif 

Tips:无论静态库还是动态库都需要用到*.lib文件,前者直接注入可执行程序中,后者用于揭示可执行运行时应该如何找到自己想要的接口(功能实现)。如果不使用__declspec,默认不会进行任何符号导出,编译器自然不会生成任何.lib文件。

cmake_minimum_required(VERSION 3.15)
project(buildLib)
set(LIBRARY_OUTPUT_PATH ../lib)
add_library(myLib SHARED library.cpp)

没有加入编译宏,因此只生成了*.dll文件:
在这里插入图片描述
使用这个库的程序,编译时会出现“文件无效或损坏”的错误:
在这里插入图片描述
出现这个的原因是因为没有提供有效的调用动态库内功能的信息文件(*.lib)。加上编译宏BUILD_DLL

cmake_minimum_required(VERSION 3.15)
project(buildLib)
set(LIBRARY_OUTPUT_PATH ../lib)
add_library(myLib SHARED library.cpp)
add_compile_definitions(BUILD_DLL)				#注意不要再加-D了

可以看到,在lib目录下既生成了*.lib,也生成了*.dll
在这里插入图片描述
使用者使用动态库的时候,直接链接*.lib进行编译:
在这里插入图片描述
运行的时候什么都没有打印,这是因为可执行程序不知道去哪里搜索所需动态库(为啥没点提示,困惑),利用VS开发工具可以查看依赖情况:
在这里插入图片描述
Window动态库搜索顺序[4]:

  • 已知动态库,如Kernel32.dll User32.dll
  • 当前进程可执行模块所在目录
  • 当前目录
  • Windows系统目录。使用GetSystemDirectory函数获取这个目录(C:\Windows\system32)
  • Windows目录。使用GetWindowsDirectory函数获取这个目录(C:\Windows)
  • PATH环境变量

所以只要将我们生成的动态库放到任意一个搜索路径即可,这里我直接放在了与可执行程序同级目录,再次运行程序,结果如下:
在这里插入图片描述
至此,动态库创建和使用已经成功。

四、创建并使用静态库

4.1 生成静态库

使用静态库默认符号全部导出,所以不需要特殊处理。CMake的内容如下:

cmake_minimum_required(VERSION 3.15)
project(buildLib)
set(LIBRARY_OUTPUT_PATH ../lib)
add_library(myLib STATIC library.cpp)

新建一个文件夹,名为build(VSCODE默认会帮你做好这些事情,你只需要将CMakeLists.txt放在源码同级即可),然后cmake ..,新生成的静态库在编译目录的上一级名叫lib的文件夹。如下:在这里插入图片描述

4.2 使用这个静态库

新建一个文件夹,内含一个主函数,功能是使用我们刚刚新建的静态库。

//main.cpp
#include "library.h"
#include <iostream>

int main()
{
    hello();
    std::cout<<"the result is 1+2="<<myadd(1,2)<<std::endl;
    return 0;
}

在CMakeLists.txt中增加库的头文件搜寻目录,指出具体的库文件名称(绝对、相对均可)或者增加库搜寻目录,这里用的绝对路径。CMakeLists.txt如下:

project(UseLib)
cmake_minimum_required(VERSION 3.15)
include_directories(C:\\Users\\Fred\\buildLib)
add_executable(Demo main.cpp)
target_link_libraries(Demo C:\\Users\\Fred\\buildLib\\lib\\Debug\\mylib.lib)

ok,正确输出了结果,说明调用没有问题。

在这里插入图片描述


[1] https://docs.microsoft.com/zh-cn/cpp/build/linking-an-executable-to-a-dll?view=msvc-170
[2] https://docs.microsoft.com/zh-cn/cpp/cpp/dllexport-dllimport?view=msvc-170
[3] https://www.kitware.com/create-dlls-on-windows-without-declspec-using-new-cmake-export-all-feature/
[4] https://docs.microsoft.com/en-us/previous-versions/7d83bc18(v=vs.140)?redirectedfrom=MSDN

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值