• 动态链接库(四)--优化动态链接库的创建和使用


    写在前面

    之前的DLL1项目动态链接库的创建只有一个cpp源文件,里面包含函数的声明和实现,这样的方式生成lib和dll后,再添加到其他项目时,还需手动的在项目添加导出函数声明.

    还好我们的DLL1项目只有两个导出函数,若要有几十上百个,查找改编前的函数名,再手动添加导入声明,属实麻烦.

    这里优化的方式就是为我们的DLL1项目添加一个Dll1.h头文件, 在开发DLL时,同步的更新Dll1.h中的声明,这样别人使用我们的Dll1.dll时,除了lib和dll文件外,再在项目中包含dll1.h头文件即可,而无需另外声明.

    注意: 后续示例均已隐式导入为例,因前面动态链接库的使用已详细说明的如何导入,因此后续更新dll时步骤不再详细介绍.

    补充: _declspec 和 __declspec 的区别

    即单下划线和双下划线的区别. 通过MSDN可以得知,双下划线的__declspec才是标准的C++关键字,当然在VS中_declspec也没有问题,两个功能一致,但后续使用上尽量还是使用双下划线的版本:
    1

    优化动态链接库的创建和使用

    通常在编写动态链接库时,都会提供一个头文件,在此文件中提供DLL导出函数原型的声明以及函数的有关注释文档.

    这里我们为DLL1项目添加一个头文件Dll1.h:
    2
    因为Dll1.h头文件是对外的,即提供给其他项目包含的,因此这里是**__declspec(dllimport)**而不是__declspec(dllexport).

    在DLL_test中添加Dll1.h头文件,包含代替此前的手动声明即可:
    3

    这里的Dll1.h头文件仅对外使用,可以直接在DLL1项目内部使用吗?即头文件声明,源文件实现:
    4
    5

    重新生成,会看到如下提示:
    6

    因为这是一个DLL项目,Dll1.cpp中的实现要么是不导出函数,要么是导出函数,不能是导入函数,所以这里会这样提示,但实现生成的dll中还是会有这两个导出函数:
    7
    相当于忽略了Dll1.h中的声明了.

    可以发现这样声明的Dll1.h头文件只能对外使用,而不能对内使用,主要原因就是对外时必须是__declspec(dllimport),对内时必须是__declspec(dllexport).

    这里可以使用宏定义来解决这个问题,区分包含Dll1.h头文件的项目是生成DLL的项目还是使用DLL的项目.

    修改Dll1.h头文件如下:
    8
    头文件中使用自定义的宏DLL1_API 来代替标识符__declspec(dllimport).

    在头文件中,我们首先使用条件编译判断是否定义了DLL1_API 符号,如果已经定义了该符号,那么就不做任何处理;否则定义该符号为_declspec(dllimport) ,告诉编译器该函数是从动态库中引入的.

    同时修改Dll1.cpp源文件如下:
    9
    在动态库的源程序Dll1.cpp中,首先利用#define 定义DLL1_API宏,然后包含上面头文件. 之后在源文件中定义函数时就不用_declspec(dllexport) 标识符了.

    因为在该DLL程序编译时,头文件不参与编译,源文件单独编译.

    因此在编译Dll1.cpp时,首先定义DLL1_API 宏,将其定义成:__declspec(dllexport).

    然后再包含Dll1.h,这时将展开该头文件,判断DLL1_API宏是否已经定义,此时DLL1_API 已定义为__declspec(dllexport),所以直接编译其后的两个函数的声明.

    因为在头文件的两个函数的声明中使用了DLL1_API 宏(__declspec(dllexport)),所以这里相当于__declspec(dllexport) int add(int a, int b); 即表明这两个函数是动态链接库的导出函数.

    之后将这个DLL交给其他程序使用,后者有引用Dll1.h 头文件,展开后DLL1_API宏没有定义,那么就将该宏定义成__declspec(dllimport), 告诉编译器这两个函数是从DLL动态库中引入的,即add和subtract函数是导入函数.

    重新生成dll,更新到DLL_test 中,隐式加载所需文件:Dll1.lib,Dll1.dll以及Dll1.h
    10

    重新编译运行如下:
    11

    注意事项

    这里的技巧是将DLL1_API宏定义在cpp文件中,在包含头文件:
    12

    而不是这样:
    13 14
    首先这里宏重定义了,会取最后一次定义为准。
    这里对内使用时不会有差异,关键是对外使用时会有问题,例在DLL_test中再次定义宏:
    15

    在DLL_test项目中的Dll1.h头文件中的类型为__declspec(dllexport)了, 发现依旧可以正常调用:
    16
    这是因为虽然当前Dll1.h声明为__declspec(dllexport), 但Dll1.dll中的函数已经是__declspec(dllexport)的了,所以这里的声明只是起到了提示作用,并不影响实际已经生成的导出函数,所以才能正常调用.

    虽然不会影响使用,但就可读性来说还是会对后续维护人员有一定影响,所以后续还是使用之前的定义方式较为稳妥,即:
    17
    18

    补充

    对于DLL1_API宏的定义,也可在项目属性中配置,来代替Dll1.cpp源文件中的定义,以避免忘记定义的问题.

    右键Dll1项目 –》C/C++ -》预处理器 –》编辑,添加DLL1_API,如下:
    19
    这里只是定义而没有值,所以并不适用上面的第一种方式。

    总结

    为此前的Dll1项目添加的说明性质的Dll1.h头文件, 对内对外使用.

    注意 对DLL1_API宏 定义的位置, 可能会影响代码的可读性.

    扩展介绍了在项目属性的预处理器项中定义宏的方式, 该方式只声明, 而不提供实际值的定义.

    代码

    最后附上本文涉及代码:

    	//DLL1项目
    	//Dll1.h
    	#pragma once
    	
    	#ifdef DLL1_API
    	#else
    	#define DLL1_API __declspec(dllimport)
    	#endif
    
    	int DLL1_API add(int a, int b);
    	int DLL1_API subtract(int a, int b);
    
    	//Dll1.cpp
    	#define DLL1_API __declspec(dllexport)
    	#include "Dll1.h"
    
    	int add(int a, int b)
    	{
    		return a + b;
    	}
    
    	int subtract(int a, int b)
    	{
    		return a - b;
    	}
    
    	//DLL_test项目
    	//main.cpp
    	#include 
    	using namespace std;
    	#include "Dll1.h"
    
    	int main()
    	{
    		cout << "累加函数测试: " << add(5, 3) << endl;
    		cout << "减法函数测试: " << subtract(5, 3) << endl;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
  • 相关阅读:
    Git技法:.gitignore、移除暂存与撤销修改
    TF坐标变换
    HCIP-datacom
    PG数据库表及表注释查询语句
    SWT Table列自适应大小
    图像的几何变换(缩放、平移、旋转)
    【k8s】浅谈对kubernetes基本概念
    SFX的妙用——如何在不安装软件的情况下打开自定义格式文件?
    微信小程序 java ssm电影迷爱好者交流平台
    「PAT乙级真题解析」Basic Level 1099 性感素数 (问题分析+完整步骤+伪代码描述+提交通过代码)
  • 原文地址:https://blog.csdn.net/SNAKEpc12138/article/details/126189926