C++学习笔记-使用动态库

动态库(.dll文件)是在运行时加载的代码库,与静态库不同,动态库不会被编译到最终的可执行文件中,而是在程序运行时动态加载。这种方式可以减小可执行文件大小,并允许多个程序共享同一个库。

动态库的基本概念

 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
#include <iostream>
#include "GLFW/glfw3.h"  // 使用动态库版本的GLFW

int main()
{
    int result = glfwInit();
    std::cout << "GLFW Init result: " << result << std::endl;
    
    if (result)
    {
        std::cout << "GLFW initialized successfully!" << std::endl;
        
        // 创建窗口测试
        GLFWwindow* window = glfwCreateWindow(800, 600, "Dynamic Library Test", NULL, NULL);
        if (window)
        {
            std::cout << "Window created successfully!" << std::endl;
            glfwDestroyWindow(window);
        }
        
        glfwTerminate();
    }
    else
    {
        std::cout << "Failed to initialize GLFW" << std::endl;
    }
    
    return 0;
}

配置动态库的步骤

1. 包含头文件目录(与静态库相同)

项目 -> 属性 -> C/C++ -> 常规 -> 附加包含目录
例如:$(SolutionDir)Dependencies\GLFW\include

2. 链接导入库文件

动态库配置与静态库的不同之处:

项目 -> 属性 -> 链接器 -> 常规 -> 附加库目录
例如:$(SolutionDir)Dependencies\GLFW\lib-vc2022

项目 -> 属性 -> 链接器 -> 输入 -> 附加依赖项
静态库:glfw3.lib
动态库:glfw3dll.lib  // 注意文件名不同

3. 部署DLL文件

要确保exe能找到.dll文件,最简单的方法是将DLL文件放在exe同一个目录中:

MyProject/
├── MyProject.exe
├── glfw3.dll      // DLL文件必须在运行时可访问
└── other_files...

创建自己的动态库

数学库的动态版本

MathLibraryDLL.h

 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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#pragma once

// 导出/导入宏定义
#ifdef MATHLIBRARY_EXPORTS
    #define MATHLIB_API __declspec(dllexport)
#else
    #define MATHLIB_API __declspec(dllimport)
#endif

// 为了C兼容性,使用extern "C"
extern "C"
{
    // Vector3 C接口
    MATHLIB_API void* Vector3_Create(float x, float y, float z);
    MATHLIB_API void Vector3_Destroy(void* vector);
    MATHLIB_API void Vector3_Add(void* result, void* a, void* b);
    MATHLIB_API void Vector3_Subtract(void* result, void* a, void* b);
    MATHLIB_API void Vector3_Scale(void* result, void* vector, float scalar);
    MATHLIB_API float Vector3_Magnitude(void* vector);
    MATHLIB_API float Vector3_Dot(void* a, void* b);
    MATHLIB_API void Vector3_Cross(void* result, void* a, void* b);
    MATHLIB_API void Vector3_Print(void* vector);
    
    // 工具函数
    MATHLIB_API float ToRadians(float degrees);
    MATHLIB_API float ToDegrees(float radians);
    MATHLIB_API float Clamp(float value, float min, float max);
    MATHLIB_API float Lerp(float a, float b, float t);
}

// C++接口(可选)
#ifdef __cplusplus
namespace MathLibDLL
{
    class MATHLIB_API Vector3
    {
    private:
        float m_X, m_Y, m_Z;
        
    public:
        Vector3(float x = 0, float y = 0, float z = 0);
        ~Vector3();
        
        // 访问器
        float GetX() const { return m_X; }
        float GetY() const { return m_Y; }
        float GetZ() const { return m_Z; }
        
        void SetX(float x) { m_X = x; }
        void SetY(float y) { m_Y = y; }
        void SetZ(float z) { m_Z = z; }
        
        // 运算符重载
        Vector3 operator+(const Vector3& other) const;
        Vector3 operator-(const Vector3& other) const;
        Vector3 operator*(float scalar) const;
        
        // 数学运算
        float Magnitude() const;
        Vector3 Normalize() const;
        float Dot(const Vector3& other) const;
        Vector3 Cross(const Vector3& other) const;
        
        void Print() const;
    };
    
    // 工具函数的C++版本
    MATHLIB_API float ToRadians(float degrees);
    MATHLIB_API float ToDegrees(float radians);
    MATHLIB_API float Clamp(float value, float min, float max);
    MATHLIB_API float Lerp(float a, float b, float t);
}
#endif

MathLibraryDLL.cpp

  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
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include "MathLibraryDLL.h"
#include <iostream>
#include <cmath>

// C接口实现
extern "C"
{
    void* Vector3_Create(float x, float y, float z)
    {
        return new MathLibDLL::Vector3(x, y, z);
    }
    
    void Vector3_Destroy(void* vector)
    {
        delete static_cast<MathLibDLL::Vector3*>(vector);
    }
    
    void Vector3_Add(void* result, void* a, void* b)
    {
        auto* res = static_cast<MathLibDLL::Vector3*>(result);
        auto* vecA = static_cast<MathLibDLL::Vector3*>(a);
        auto* vecB = static_cast<MathLibDLL::Vector3*>(b);
        
        *res = *vecA + *vecB;
    }
    
    float Vector3_Magnitude(void* vector)
    {
        auto* vec = static_cast<MathLibDLL::Vector3*>(vector);
        return vec->Magnitude();
    }
    
    void Vector3_Print(void* vector)
    {
        auto* vec = static_cast<MathLibDLL::Vector3*>(vector);
        vec->Print();
    }
    
    float ToRadians(float degrees)
    {
        return degrees * 3.14159f / 180.0f;
    }
    
    float ToDegrees(float radians)
    {
        return radians * 180.0f / 3.14159f;
    }
    
    float Clamp(float value, float min, float max)
    {
        if (value < min) return min;
        if (value > max) return max;
        return value;
    }
    
    float Lerp(float a, float b, float t)
    {
        return a + (b - a) * Clamp(t, 0.0f, 1.0f);
    }
}

// C++接口实现
namespace MathLibDLL
{
    Vector3::Vector3(float x, float y, float z) : m_X(x), m_Y(y), m_Z(z) {}
    
    Vector3::~Vector3() {}
    
    Vector3 Vector3::operator+(const Vector3& other) const
    {
        return Vector3(m_X + other.m_X, m_Y + other.m_Y, m_Z + other.m_Z);
    }
    
    Vector3 Vector3::operator-(const Vector3& other) const
    {
        return Vector3(m_X - other.m_X, m_Y - other.m_Y, m_Z - other.m_Z);
    }
    
    Vector3 Vector3::operator*(float scalar) const
    {
        return Vector3(m_X * scalar, m_Y * scalar, m_Z * scalar);
    }
    
    float Vector3::Magnitude() const
    {
        return std::sqrt(m_X * m_X + m_Y * m_Y + m_Z * m_Z);
    }
    
    Vector3 Vector3::Normalize() const
    {
        float mag = Magnitude();
        if (mag > 0)
            return Vector3(m_X / mag, m_Y / mag, m_Z / mag);
        return Vector3();
    }
    
    float Vector3::Dot(const Vector3& other) const
    {
        return m_X * other.m_X + m_Y * other.m_Y + m_Z * other.m_Z;
    }
    
    Vector3 Vector3::Cross(const Vector3& other) const
    {
        return Vector3(
            m_Y * other.m_Z - m_Z * other.m_Y,
            m_Z * other.m_X - m_X * other.m_Z,
            m_X * other.m_Y - m_Y * other.m_X
        );
    }
    
    void Vector3::Print() const
    {
        std::cout << "(" << m_X << ", " << m_Y << ", " << m_Z << ")" << std::endl;
    }
}

使用动态库

C++接口使用方式

 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
#include <iostream>
#include "MathLibraryDLL.h"

int main()
{
    using namespace MathLibDLL;
    
    std::cout << "=== Dynamic Library C++ Interface Test ===" << std::endl;
    
    Vector3 v1(1.0f, 2.0f, 3.0f);
    Vector3 v2(4.0f, 5.0f, 6.0f);
    
    std::cout << "v1: ";
    v1.Print();
    std::cout << "v2: ";
    v2.Print();
    
    Vector3 sum = v1 + v2;
    std::cout << "v1 + v2: ";
    sum.Print();
    
    float dot = v1.Dot(v2);
    std::cout << "v1 · v2 = " << dot << std::endl;
    
    std::cout << "v1 magnitude: " << v1.Magnitude() << std::endl;
    
    // 使用工具函数
    float radians = ToRadians(90.0f);
    std::cout << "90 degrees = " << radians << " radians" << std::endl;
    
    return 0;
}

C接口使用方式

 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
#include <iostream>
#include "MathLibraryDLL.h"

int main()
{
    std::cout << "=== Dynamic Library C Interface Test ===" << std::endl;
    
    // 使用C接口
    void* v1 = Vector3_Create(1.0f, 2.0f, 3.0f);
    void* v2 = Vector3_Create(4.0f, 5.0f, 6.0f);
    void* result = Vector3_Create(0.0f, 0.0f, 0.0f);
    
    std::cout << "v1: ";
    Vector3_Print(v1);
    std::cout << "v2: ";
    Vector3_Print(v2);
    
    Vector3_Add(result, v1, v2);
    std::cout << "v1 + v2: ";
    Vector3_Print(result);
    
    float magnitude = Vector3_Magnitude(v1);
    std::cout << "v1 magnitude: " << magnitude << std::endl;
    
    // 清理资源
    Vector3_Destroy(v1);
    Vector3_Destroy(v2);
    Vector3_Destroy(result);
    
    return 0;
}

运行时动态加载

 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
38
39
40
41
42
#include <iostream>
#include <windows.h>

// 函数指针类型定义
typedef float (*ToRadiansFunc)(float);
typedef float (*ClampFunc)(float, float, float);

int main()
{
    std::cout << "=== Runtime Dynamic Loading ===" << std::endl;
    
    // 加载DLL
    HMODULE hModule = LoadLibrary(L"MathLibraryDLL.dll");
    if (!hModule)
    {
        std::cout << "Failed to load DLL" << std::endl;
        return -1;
    }
    
    // 获取函数地址
    ToRadiansFunc toRadians = (ToRadiansFunc)GetProcAddress(hModule, "ToRadians");
    ClampFunc clamp = (ClampFunc)GetProcAddress(hModule, "Clamp");
    
    if (!toRadians || !clamp)
    {
        std::cout << "Failed to get function addresses" << std::endl;
        FreeLibrary(hModule);
        return -1;
    }
    
    // 使用函数
    float radians = toRadians(90.0f);
    std::cout << "90 degrees = " << radians << " radians" << std::endl;
    
    float clamped = clamp(15.0f, 0.0f, 10.0f);
    std::cout << "Clamp(15, 0, 10) = " << clamped << std::endl;
    
    // 卸载DLL
    FreeLibrary(hModule);
    
    return 0;
}

跨平台动态库加载

 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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <iostream>

#ifdef _WIN32
    #include <windows.h>
    #define LOAD_LIBRARY(name) LoadLibrary(name)
    #define GET_PROC_ADDRESS(module, name) GetProcAddress(module, name)
    #define FREE_LIBRARY(module) FreeLibrary(module)
    typedef HMODULE LibraryHandle;
#else
    #include <dlfcn.h>
    #define LOAD_LIBRARY(name) dlopen(name, RTLD_LAZY)
    #define GET_PROC_ADDRESS(module, name) dlsym(module, name)
    #define FREE_LIBRARY(module) dlclose(module)
    typedef void* LibraryHandle;
#endif

class DynamicLibrary
{
private:
    LibraryHandle m_Handle;
    
public:
    DynamicLibrary(const char* libraryName) : m_Handle(nullptr)
    {
#ifdef _WIN32
        // Windows: 转换为宽字符
        int size = MultiByteToWideChar(CP_UTF8, 0, libraryName, -1, nullptr, 0);
        wchar_t* wideLibraryName = new wchar_t[size];
        MultiByteToWideChar(CP_UTF8, 0, libraryName, -1, wideLibraryName, size);
        m_Handle = LOAD_LIBRARY(wideLibraryName);
        delete[] wideLibraryName;
#else
        m_Handle = LOAD_LIBRARY(libraryName);
#endif
    }
    
    ~DynamicLibrary()
    {
        if (m_Handle)
        {
            FREE_LIBRARY(m_Handle);
        }
    }
    
    bool IsLoaded() const
    {
        return m_Handle != nullptr;
    }
    
    template<typename T>
    T GetFunction(const char* functionName)
    {
        if (!m_Handle)
            return nullptr;
            
        return reinterpret_cast<T>(GET_PROC_ADDRESS(m_Handle, functionName));
    }
};

int main()
{
    std::cout << "=== Cross-Platform Dynamic Loading ===" << std::endl;
    
#ifdef _WIN32
    DynamicLibrary lib("MathLibraryDLL.dll");
#else
    DynamicLibrary lib("libMathLibrary.so");
#endif
    
    if (!lib.IsLoaded())
    {
        std::cout << "Failed to load library" << std::endl;
        return -1;
    }
    
    // 获取函数指针
    auto toRadians = lib.GetFunction<float(*)(float)>("ToRadians");
    auto clamp = lib.GetFunction<float(*)(float, float, float)>("Clamp");
    
    if (toRadians && clamp)
    {
        float radians = toRadians(45.0f);
        std::cout << "45 degrees = " << radians << " radians" << std::endl;
        
        float clamped = clamp(7.5f, 0.0f, 10.0f);
        std::cout << "Clamp(7.5, 0, 10) = " << clamped << std::endl;
    }
    else
    {
        std::cout << "Failed to get function pointers" << std::endl;
    }
    
    return 0;
}

动态库的版本管理

 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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// 在DLL中导出版本信息
extern "C"
{
    MATHLIB_API int GetMajorVersion();
    MATHLIB_API int GetMinorVersion();
    MATHLIB_API int GetPatchVersion();
    MATHLIB_API const char* GetVersionString();
    MATHLIB_API bool IsCompatibleVersion(int major, int minor);
}

// 实现
int GetMajorVersion() { return 1; }
int GetMinorVersion() { return 2; }
int GetPatchVersion() { return 3; }

const char* GetVersionString()
{
    static char version[32];
    sprintf_s(version, "%d.%d.%d", GetMajorVersion(), GetMinorVersion(), GetPatchVersion());
    return version;
}

bool IsCompatibleVersion(int major, int minor)
{
    return (major == GetMajorVersion()) && (minor <= GetMinorVersion());
}

// 使用版本检查
int main()
{
    DynamicLibrary lib("MathLibraryDLL.dll");
    
    if (lib.IsLoaded())
    {
        auto getVersion = lib.GetFunction<const char*(*)()>("GetVersionString");
        auto isCompatible = lib.GetFunction<bool(*)(int, int)>("IsCompatibleVersion");
        
        if (getVersion && isCompatible)
        {
            std::cout << "Library version: " << getVersion() << std::endl;
            
            if (isCompatible(1, 2))
            {
                std::cout << "Version is compatible" << std::endl;
                // 继续使用库...
            }
            else
            {
                std::cout << "Version is not compatible" << std::endl;
                return -1;
            }
        }
    }
    
    return 0;
}

总结

  1. 动态库配置:需要链接导入库(.lib),并确保运行时能找到DLL文件
  2. 动态库特点
    • 运行时加载,可执行文件较小
    • 多个程序可共享同一个DLL
    • 可以独立更新库文件
    • 需要处理DLL部署问题
  3. 接口设计:提供C接口增强兼容性,C++接口提供更好的易用性
  4. 运行时加载:可以在程序运行时动态加载和卸载库
  5. 版本管理:导出版本信息,进行兼容性检查
updatedupdated2025-09-202025-09-20