C++: 静态链接库与动态链接库的使用

什么是链接库

库(库文件):计算机中有些文件专门用于存储可以重复使用的代码块。

//函数库
int add(int a,int b)
{
    return a + b;
}

上述函数库为源代码库,该库文件的二进制版本——链接库。

链接库,就是将开源的库文件进行编译、打包操作后得到的二进制文件,二进制文件无法独立运行,必须等待其他程序调用才会被载入内存。

  • 编译:生成多个二进制目标文件,它们之间会相互调用对方的函数或变量,编译器无法跨文件找到对应存储地址,故无法单独执行;
  • 链接:链接器修复各个目标文件中缺失的函数和变量的存储地址,并最终将所有目标文件和链接库组织成一个可执行文件

静态链接(完成所有可执行文件链接操作再载入内存)

  1. 形成可执行程序前对所有 *.o 目标文件进行对应链接文件地址查找以形成可执行程序;

  2. 多个目标文件的形成使生成多个具有共性(同个函数)的副本,加大了容量,同时进行更新修改时需要重新进行可执行程序的重新生成,但对于可执行程序是优势的,全面的程序执行所需要的内容,使得执行速度更快。

动态链接(动态链接库随着可执行文件的需要而载入内存)

  • 链接器先从所有目标文件找到对应目标文件部分缺失的地址,然后组织成一个可执行程序,待文件执行时,连同所有的链接库文件一起载入内存,再由链接器完成剩余的地址修复工作,从而正常执行。

优点:

  1. 节省内存和代码重用:多个应用程序共享磁盘上单个DLL副本;
  2. 可扩展性:接口不变;
  3. 更新程序只需更新DLL文件;
  4. 复用性:不同语言编写的程序只需要按照函数调用约定就可以调用同个DLL函数。

链接库的区别

静态链接库

*.h 文件和 .lib文件:

  1. *.h 文件:函数声明与参数定义等;
  2. *.lib 文件:包含函数的索引以及实现等;(Linux下是 .so 文件)
  3. 语法:(预处理阶段就被替换)

#include"头文件" #pragma comment(lib, "./头文件.lib")

动态链接库

*.h 文件、 *.lib 文件、 *.dll 文件:

  1. *.lib 文件:存储链接程序所必须的信息,并没有存储函数的实现等;
  2. *.dll 文件:存储函数的具体实现,程序运行加载时载入内存以充分运行;

*.dll 文件存放位置:

  1. 解决方案下的 debug 文件夹,同程序 exe 一块;
  2. 与 .h .lib .dll .cpp 文件同个位置;
  3. 系统目录下:system32 或 system64 文件夹;
  4. 环境变量所列出的路径下。
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"

BOOL APIENTRY DllMain(HMODULE hModule,  //模块调用
    DWORD  ul_reason_for_call,          //调用原因
    LPVOID lpReserved                   //保留参数 不用关心
    )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:       //被其他程序加载时候
        MessageBox(NULL, L"加减乘除DLL被加载", L"l love Mark", MB_OK);
        break;
    case DLL_THREAD_ATTACH:      //当其他程序启动了一个线程的时候
        break;
    case DLL_THREAD_DETACH:      //当其他程序某个线程线程终止运行的时候
        break;
    case DLL_PROCESS_DETACH:     //被其他程序卸载的时候
        MessageBox(NULL, L"加减乘除DLL被卸载", L"l love Mark", MB_OK);
        break;
    }
    return TRUE;
}

某个动态链接库的头文件:

#ifdef DLL1_API
#else 
#define DLL1_API _declspec(dllimport)   
#endif

//所有的函数一定要设置成为导出类型,否则无法生成lib文件,不存在导出的函数
DLL1_API double add(double a, double b);
DLL1_API double subtract(double a, double b);
DLL1_API double multiply(double a, double b);
DLL1_API double chu(double a, double b);

class DLL1_API Point
{
  public:
      void output(int x, int y);
};

使用链接库

静态链接库

1、.lib 文件和 .h 文件:同时包含到使用项目下,再进行代码调用: #include "static.h" #pragma comment(lib, "static.lib")。

2、将头文件 .h 放进自定义路径 include 包含目录,

​ 将库文件 .lib 放进自定义路径 lib 库目录,

​ 选中项目进行属性“VC++ 目录 —— 常规”的包含目录和库目录的路径编辑添加,

​ 同时“链接器 —— 输入”的附加依赖项添加 .lib 文件全名,

​ 回到代码调用:添加对应静态库指定的头文件 #include "static.h"。

3、(1)引用添加 .lib 文件,

​ (2)在项目属性“C/C++ —— 常规”的附加包含目录下添加 .lib 文件目录,

​ (3)回到代码调用:添加对应静态库指定的头文件 #include "static.h"。

动态链接库

1、隐式调用:需要 .h .lib .dll

​ (1)将 DLL 过程中的 .h、.lib以及 .dll 文件复制到与项目同名文件夹下;

​ (2)项目添加上述文件,再进行代码调用:
#include "MyDll.h" #pragma comment(lib,"MyDll.lib")

或者:

​ (1)在项目属性“C/C++ —— 常规”的附加包含目录下添加 .lib 文件目录, (2)在项目属性“链接器 —— 输入”的附加依赖项下添加 .lib 文件全名, (3)在项目属性“链接器 —— 常规”的附加库目录下添加 .lib 文件的完全路径, (4)回到代码调用:

​ /#include "MyDll.h"

2、显式调用:只需要 .dll

(1)创建别名,实现将动态库函数导出

(2)定义实例句柄,获取 DLL 的实例地址,再引用动态库

(3)加载动态库路径,写入句柄(取的地址要判断,确认是否返回为空 / 无效句柄)

(4)定义函数指针,指向动态库路径导出函数

(5)获取动态库对应函数名称的地址,使得函数指针正确指向该函数

(6)判断函数指针是否有效,有效则进入函数调用

(7)释放动态句柄

#include <iostream>

#include <Windows.h>

using namespace std;

// 1、创建别名,引用函数类型需要和 dll 中的导出函数一致
typedef int (*LPDO_ARRAY)(int* pArr, int nlen);

int main()
{
    // 定义实例句柄,引用动态库
    HINSTANCE hDll;
    // 定义函数指针,指向导出函数
    LPDO_ARRAY lpDo_array;

    // 2、加载动态库文件
    hDll = LoadLibrary(L"BuildDllDemo1.dll");
    // 3、获取 dll 中函数的地址
    lpDo_array = (LPDO_ARRAY)GetProcAddress(hDll, "convert_array");
    if (lpDo_array != NULL || lpDo_array != 0)
    {
        // 4、调用函数
        /// ...TODO
        // 5、释放动态库句柄
        FreeLibrary(hDll);
    }

    return 0;
}

动态链接库的导出

宏定义

#ifdef XXX_DLL_EXPORTS
#define XXX_DLL_API __declspec(dllexport)
#else
#define XXX_DLL_API __declspec(dllimport)
#endif

导出类

1、__declspec(dllexport)

方式简单,导出的内容多,使用着对类的实现依赖太多,还需保证使用同一编译器。

本质:导出类里面的函数,因为语法上直接导出了类,没有对函数的调用方式和重命名进行设置,导致了 dll 并不通用。

struct CEMPLOYEETOMOM_DLL_API CEMPLOYEETOMOM_STT
{
    int a; 
}

class XXX_DLL_API CEmployeeToMOM
{
private:
    ...
public:
    ...
}

2、对象类指针接口

定义抽象类(纯虚函数),调用者跟 dll 共用一个抽象类的头文件。

DLL 实现抽象类的派生类,最少只需要提供一个用于获取抽象类对象指针的接口。

/// a.h
class VirtualBin
{
public:
    VirtualBin() {}
    virtual ~VirtualBin(){}
    virtual double Add(double a, double b) = 0;
};

/// a1.h
class BinClass:public VirtualBin
{
public:
    double Add(double a, double b);
};
/// a1.cpp
double BinClass::Add(double a, double b)
{
    return a + b;
}
BinClass* __stdcall CreateClass(const char * s)
{
    if (_stricmp(s, ("BinClass")) == 0)
    {
        return new BinClass;
    }
    return NULL;
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++中的静态链接库动态链接库的生成方法略有不同。下面分别介绍: 1. 静态链接库(Static Library) 静态链接库是指在编译链接阶段将库的代码复制到可执行文件中,因此可执行文件不再依赖于库文件,可以单独运行。静态链接库的生成方法如下: - 编写库的源代码,生成库的目标文件(.o)。 - 将所有目标文件打包成静态链接库文件(.a)。 具体的步骤如下: 首先编译库的源代码,生成目标文件(.o): ``` g++ -c lib.cpp -o lib.o ``` 然后将目标文件打包成静态链接库文件(.a): ``` ar rcs lib.a lib.o ``` 其中,`ar`命令用于打包目标文件,`rcs`参数表示创建库文件并插入目标文件,`lib.a`为库文件名,`lib.o`为目标文件名。 2. 动态链接库(Dynamic Library) 动态链接库是指在程序运行时加载库文件,因此可执行文件依赖于库文件,需要和库文件一起运行。动态链接库的生成方法如下: - 编写库的源代码,生成库的目标文件(.o)。 - 将所有目标文件编译为共享对象文件(.so)。 具体的步骤如下: 首先编译库的源代码,生成目标文件(.o): ``` g++ -c -fPIC lib.cpp -o lib.o ``` 其中,`-fPIC`参数表示生成位置无关代码,是动态链接库的必备参数。 然后将目标文件编译为共享对象文件(.so): ``` g++ -shared -o lib.so lib.o ``` 其中,`-shared`参数表示生成共享对象文件,`lib.so`为共享对象文件名,`lib.o`为目标文件名。 以上就是C++中生成静态链接库动态链接库的方法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值