? 库是写好的现有的,成熟的,可以复用的代码。库的存在形式本质上来说库是一种可执行代码的二进制。
? 库有两种:静态库(.a、.lib)和动态库(.so、.dll)。所谓静态、动态是指链接阶段,静态库的链接阶段是在项目编译的时候静态链接。动态库是在程序运行的时候由代码加载链接。
? 静态库和动态库的区别主要是在链接阶段处理库的方式不同而区分的。
? 静态库指的是在链接阶段直接将库和目标文件一起打包成可执行文件的方式所使用的库就称为静态库。
? .a 或者 .lib 文件
? 优点 :①使可执行文件依赖项少,已经被打包到可执行文件中了。 ②编译阶段完成链接,执行期间代码装载速度快
? 缺点:①使可执行文件变大。②若作为其他库的依赖库,将会造成多余的副本,因为必须与目标文件打包。③升级不方便,升级必须重新编译
例程:
#pragma once
#include <iostream>
class Arithmetic
{
public:
// Returns a + b
static double Add(double a, double b);//可以调用
// Returns a - b
static double Subtract(double a, double b);//可以调用
// Returns a * b
static double Multiply(double a, double b);//可以调用
// Returns a / b
static double Divide(double a, double b);//可以调用
void fun2();//可以调用
private:
static void fun();//不可以调用
void fun1();//不可以调用
};
#include "Arithmetic.h"
double Arithmetic::Add(double a, double b)
{
return a + b;
}
double Arithmetic::Subtract(double a, double b)
{
return a - b;
}
double Arithmetic::Multiply(double a, double b)
{
return a * b;
}
double Arithmetic::Divide(double a, double b)
{
return a / b;
}
void Arithmetic::fun2() {
std::cout << "公共 void Arithmetic::fun2()" << std::endl;
}
void Arithmetic::fun() {
std::cout << "私有 static void Arithmetic::fun()" << std::endl;
}
void Arithmetic::fun1() {
std::cout << "私有 void Arithmetic::fun1()" << std::endl;
}
动态库指的是在程序运行过程中动态加载库的方式使用的库,也就是动态库的链接是发生在程序运行时期的,它和可执行文件是分开的,只是可执行文件在运行的某个时期调用了它。
? .DLL、.lib 和 .a 、.so ;
? 好处:程序自身的体积不会因为动态函数库变大。
? 缺点:就是程序运行过程中使用到了这些函数库内的功能时,万一系统特定的位置没有对应的动态库。就会造成程序崩溃或者各种奇怪的问题。
在介绍动态库的创建之前,我们先来了解以下的宏。
#pragma once
#ifdef PUBFUN_EXPORTS
#define PUBUTIL_DLL __declspec(dllexport)
#else
#define PUBUTIL_DLL __declspec(dllimport)
#endif
MSVC编译器提供了一系列C/C++的扩展来指定符号的导入导出,即__declspec属性关键字。
dllexport与dllimport存储级属性是微软对C和C++的扩展,可用于从dll中导入或导出函数、数据、对象(objects)
__declspec(dllexport) 表示该符号是从本DLL导出的符号。
__declspec(dllimport) 表示该符号是从别的DLL中导入的。
我们在创建动态库的时候需要用到上面的宏。
创建方式1:使用空项目
例程:
创建动态库导出导入宏
//header.h
#pragma once
#ifdef BD_TEST
#define BDTEXT_DLL __declspec(dllimport)
#else
#define BDTEST_DLL __declspec(dllexport)
#endif
使用宏定义导出的函数
//testDLL.h
#pragma once
#include "header.h"
int BDTEXT_DLL add(int a, int b);
导出函数功能的实现
//testDLL.cpp
#include "testDLL.h"
int add(int a, int b) {
return a + b;
}
编译:
这就是最后编译出来的lib和dll库。
创建方式2:直接创建动态库项目
使用直接创建动态库项目,会在项目中生成一个dllMain.cpp的文件。如下:
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
根据微软官网说明:
DllMain是动态链接库 (DLL) 的可选入口点。 当系统启动或终止进程或线程时,它将使用进程的第一个线程为每个加载的 DLL 调用入口点函数。 使用 LoadLibrary 和 FreeLibrary 函数加载或卸载 DLL 时,系统还会调用 DLL 的入口点函数。
之后其他的接口函数和接口类和空项目的建立是一样的。
例程:
配置头文件
配置lib文件位置
配置需要使用lib文件
配置完成之后编写代码
#include <iostream>
#include <testDLL.h>//引入头文件
int main() {
std::cout << add(2, 5) << std::endl; //调用库中的函数
getchar();
return 0;
}
注意点:
1、没有设置为导出的函数名称或者类,无法在外部调用dll使用。导出的函数和类必须使用导出宏修饰。
常规 -> 输出目录 :指的是dll输出的文件位置
常规 -> 中间目录 :指的是中间件的输出位置
常规 -> 配置类型 :指的是项目类型
VC++目录 -> 包含文件 :指的是三方库文件的头文件位置
VC++目录 -> 库目录 : 指的是三方库文件(.dll .lib)文件
C/C++ -> 常规 -> 附加包含目录 :指的是需要使用的自己编写的头文件
C/C++ -> 预处理器 -> 预处理器定义 : 指的是我们需要配置的宏定义
链接器 -> 输入 -> 附加依赖项 :指的是在调用的时候声名需要使用的lib文件。
链接器 -> 高级 -> 导入库 : 指的是lib导出的文件位置