库的基本认识
静态库(Static Library)
- 基本概念:静态库是在编译时链接到目标程序中的库文件。它包含了程序运行所需的所有函数和数据,这些函数和数据会被直接嵌入到最终生成的可执行文件中。静态库通常以
.a
(在Unix-like系统中)或.lib
(在Windows系统中)为文件扩展名。 - 作用:
- 代码重用:静态库允许开发者将常用的代码或功能封装成库,以便在多个项目或模块中重复使用。
- 隐藏实现细节:通过静态库,开发者可以隐藏某些函数或数据的实现细节,只提供必要的接口供其他开发者使用。
- 减小可执行文件大小:虽然静态库本身较大,但多个使用相同静态库的可执行文件在磁盘上的总存储空间可能会减小,因为它们共享相同的代码段。
- 增加编译时间:由于静态库在编译时链接到目标程序中,因此每次编译都会重新处理库中的代码,这可能导致编译时间增加。
- 不便于更新:如果静态库中的代码需要更新,那么所有使用该库的可执行文件都需要重新编译。
动态库(Dynamic Library)
- 基本概念:动态库是在运行时链接到目标程序中的库文件。它包含了程序运行所需的一些函数和数据,但这些函数和数据在程序运行时才会被加载到内存中。动态库通常以
.so
(在Unix-like系统中)或.dll
(在Windows系统中)为文件扩展名。 - 作用:
- 代码重用:与静态库类似,动态库也允许开发者将常用的代码或功能封装成库,以便在多个项目或模块中重复使用。
- 节省磁盘空间:由于动态库在多个可执行文件之间共享,因此可以节省磁盘空间。
- 减少内存使用:当多个程序使用相同的动态库时,它们可以共享内存中的同一份库代码,从而减少内存使用。
- 便于更新:如果动态库中的代码需要更新,只需要替换库文件即可,而无需重新编译使用该库的可执行文件。
- 增加加载时间:由于动态库在程序运行时才链接,因此可能导致程序加载时间增加。此外,如果动态库丢失或损坏,程序将无法正常运行。
总的来说,静态库和动态库各有优缺点,开发者需要根据具体需求选择合适的库类型。例如,在需要隐藏实现细节或减小可执行文件大小的场景中,可以使用静态库;而在需要节省磁盘空间、减少内存使用或便于更新的场景中,可以使用动态库。
文件结构与原理
- 在
dll
项目中,我们建议将函数、类…代码的声明放到.h
的头文件中,而它的具体实现将放到.cpp
文件中。 - 这样做不仅可以让项目的结构更加清晰,而且还起到一个很重要的作用:在
.h
头文件中的代码,可以巧妙利用宏定义,实现动态的声明——头文件为dll
项目的.cpp
文件的函数、类…声明"向外导出",又可以为未来要使用dll
的应用程序声明"向里面导入"。
dll 的创建
创建一个空项目
编写你的代码
- 在
MyDll.h
文件中编写代码:
- 想直接复制粘贴的朋友看这里:
#pragma once
#ifdef MYDLL
#define PORT __declspec(dllexport)
#else
#define PORT __declspec(dllimport)
#endif
PORT int add(int a, int b);
PORT int sub(int a, int b);
class PORT Circle
{
private:
float radius;
float area;
public:
Circle(float);
float getRadius();
float getArea();
};
- 想要研究源码的朋友看这里:
#pragma once // 预处理指令,用于确保头文件在一个编译单元中只被包含一次,助于防止重复定义和链接错误
// 下面的宏代码是用来判断,当前那个项目在使用"头文件"
/*
原理是:通过在DLL项目配置一个 MYDLL 的宏定义,而在 exe 项目里面不配置 MYDLL 的宏定义
当此头文件被DLL项目使用时,MYDLL必然是有定义的,从而执行 "#define PORT __declspec(dllexport)"这一句代码
当此头文件被exe项目使用时,MYDLL必然是没有定义,从而执行 "#define PORT __declspec(dllimport)"这一句代码
最终在不同项目下,PORT 有着不同的功能
在DLL项目里面,POET 将起到 "导出"的作用
在exe项目里面,POET 将起到 "导入"的作用
*/
#ifdef MYDLL // 如果 MYDLL 有定义,说明当前头文件是"DLL项目"在使用
#define PORT __declspec(dllexport) // 将 PORT 定义为 导出功能
#else // 如果 MYDLL 没有定义,说明当前头文件是"exe项目"在使用
#define PORT __declspec(dllimport) // 将 PORT 定义为 导入功能
#endif
// 在下面编写你的代码的"声明部分"
// 导入或导出————函数
PORT int add(int a, int b); // 详细写法:extern "C" PORT int add(int a, int b);
PORT int sub(int a, int b); // 详细写法:extern "C" PORT int sub(int a, int b);
// 导入或导出————类
class PORT Circle // 声明一个圆的类
{
private:
float radius; // 圆的半径
float area; // 圆的面积
public:
Circle(float);
float getRadius();
float getArea();
};
- 在
MyDll.cpp
文件中编写:
- 源码如下:
#include "MyDll.h"
/*
这里是 DLL 项目的具体实现:
而在 DLL 项目的属性中,我们需要确保 MYDLL 宏被定义
这通常是通过在项目配置的属性页中的“C/C++” -> “预处理器” -> “预处理器定义”中添加 MYDDL 来完成的
*/
// 实现在"头文件"里面声明的函数和类...
// 如下所示:
int add(int a, int b) // 加法
{
return a + b;
}
int sub(int a, int b) // 减法
{
return a - b;
}
Circle::Circle(float radius) // 实现一个圆
{
this->radius = radius;
this->area = 3.14 * radius * radius;
}
float Circle::getRadius()
{
return this->radius;
}
float Circle::getArea()
{
return this->area;
}
Release 模式
修改项目属性
- 配置类型:
生成 dll 文件
找到 dll 文件
- 在"解决方案" MySolution1 中,找到并打开 Release 文件夹
- 如果能够找到下面两个文件就说明,生成 dll 文件成功!
- 建议:复制这两个文件,并保存到一个新的文件夹中
找到 dll 项目的头文件
- 回到刚才的"解决方案" MySolution1 中,点击 MyDll 文件夹
- 建议:将这个头文件复制一份,保存到刚刚存放了 dll 文件的文件夹里面
dll 的三个文件
- 我们通过创建 dll 项目,做了一系列的步骤,最终就是为了得到上面的三个文件!
- 到此为止,我们就完成了 dll 文件的创建任务。
- 在上面的操作里,我们在 dll 文件里面,声明了一些函数和类,而且实现了这些函数和类
- 对于这些封装好的功能,我们如何才能使用它呢?
- 接下来,让我们一起来看看如何使用我们所编写的 dll 文件。
dll 的使用
创建一个空白的项目
- 我们将创建一个项目,作为一个应用程序 (exe),然后在这个应用程序里面,使用我们的 dll 文件
Release 模式
- 将模式修改为:Release(实现与 dll 项目统一模式)
引入必要文件
原理:
- 要使用 dll 里面封装好的实现代码,我们先要引入 dll 头文件里面的声明代码
- 也就是说,我们想要导入头文件
#include<MyDll.h>
- 但是,在当前这个"应用程序"的项目中,vs2019 是不知道我们已经写好了这个头文件的
- 所以,我们需要做一些配置,告诉 vs2019 我们的头文件放在了哪里
步骤:
- 告诉 vs2019 去哪个目录找头文件
- 告诉 vs2019 去哪个目录找 lib 文件
- 告诉 vs2019 在使用链接器进行链接时,链接哪一个 lib 文件
编写应用程序代码
- 源码:
#include<iostream>
#include<MyDll.h>
using namespace std;
int main()
{
int first, secend, radius;
cout << "请输入两个整数:";
cin >> first >> secend;
cout << "两数之和:" << add(first, secend) << endl;
cout << "两数之差:" << sub(first, secend) << endl;
cout << "请输入圆的半径:" << endl;
cin >> radius;
Circle myCircle(radius);
cout << "半径:" << myCircle.getRadius() << endl;
cout << "面积:" << myCircle.getArea() << endl;
system("pause");
return 0;
}
生成应用程序(exe)
- 生成 exe 文件
- 找到生成的 exe 文件,并将它和 dll 文件放在同一个目录下
最终文件与运行效果
- 最终只保留以下文件即可:
- 最终运行效果:
结束语:希望能够帮助到你