• C++中的静态库与动态库



    单独提这个 ,我想我们在coding过程中,可能也会知道一两个词,如 标准库、xx库等。库作为一组已编写好、组织好的、可复用的资源接口,可以被用于其他程序。很不夸张地说,很多程序都需要依赖一些底层依赖库,从而支撑它们完成一些基础工作。

    库主要分为两种类型:静态库(static library)动态库(shared library)

    静态库

    静态库是一个在链接过程中采用静态链接方式链接进可执行文件中的库文件,在静态链接方式中,可执行文件会拷贝静态库中导出的接口并使其成为它的一部分。在Windows系统中它主要是以.lib为后缀,而在Linux系统中,主要以.a为后缀。

    构建静态库

    项目结构为:

    .
    ├── CMakeLists.txt
    ├── library.h
    ├── library.cpp
    
    • 1
    • 2
    • 3
    • 4

    CMakeLists.txt中的内容为:

    cmake_minimum_required(VERSION 3.26)
    project(library)
    set(CMAKE_CXX_STANDARD 11)
    add_library(library STATIC library.cpp)
    
    • 1
    • 2
    • 3
    • 4

    library.h的内容为:

    #ifndef HELLO_LIB_LIBRARY_H
    #define HELLO_LIB_LIBRARY_H
    
    #include 
    class   Test {
    public:
        explicit Test(std::string str);
        std::string  getStr() ;
        static int getNum() ;
    private:
        std::string str_;
        static int num; // count value
    };
    
    void  print();
    #endif //HELLO_LIB_LIBRARY_H
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    library.cpp的内容为:

    #include "library.h"
    
    #include 
    
    int Test::num = 0; // init
    Test::Test(std::string str):str_(std::move(str)) {}
    std::string Test::getStr()  {
         ++num;
        return str_;
    }
     int Test::getNum(){
         return  num;
     }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    采用cmake进行构建,即可生成对应的静态库文件,在Window环境下将会生成library.lib,而在Linux环境下将会生成liblibrary.a

    随后可以在另一个项目中使用它,项目结构为:

    .
    ├── build
    ├── CMakeLists.txt
    ├── include
    │   └── library.h
    ├── lib
    │   └── library.lib
    └── src
        └── main.cc
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    CmakeLists.txt的内容为:

    cmake_minimum_required(VERSION 3.10)
    
    project(lib_test)
    
    set(CMAKE_CXX_STANDARD 11)
    include_directories(include)
    # 增加链接库的搜索路径
    link_directories(lib)
    # 链接 library库
    link_libraries(library)
    add_executable(${PROJECT_NAME} src/main.cc)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    而随后就可以在main.cc中使用它们了:

    #include
    #include"library.h"
    int main()
    {
        Test test("Hello");
        std::cout << test.getStr();
        std::cout << Test::getNum();
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    动态库

    动态库也叫做共享库,编译时并不会将所导出的接口拷贝到可执行文件中,而是在运行时才会被程序所引用。在Windows系统中它主要是以.dll为后缀,而在Linux系统中,主要以.so为后缀。需要特别注意的是,在MSVC编译器中,Windows环境下不仅生成dll后缀文件,还会生成.lib文件,该文件此刻的作用是作为一个导入库

    构建动态库

    项目结构大体上和上述提到的构建的静态库一致,但还需要修改一下CMakeLists以及library.h(针对MSVC编译器,Linux环境、MinGW-gcc不用管)。

    library.h文件:

    #ifndef HELLO_LIB_LIBRARY_H
    #define HELLO_LIB_LIBRARY_H
    
    
    #ifndef EXPORTTING
    #define DECLSPEC __declspec(dllimport)
    #else
    #define DECLSPEC __declspec(dllexport)
    #endif // EXPORTTING
    #include 
    
    class DECLSPEC  Test {
    public:
        explicit Test(std::string str);
        std::string  getStr() ;
        static int getNum() ;
    private:
        std::string str_;
        static int num; // count value
    };
    
    void DECLSPEC  print();
    #endif //HELLO_LIB_LIBRARY_H
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    需要额外的添加__declspec(dllexport)指示这个类/函数是一个可导出类或函数,以便在dll中导出它的接口同时在lib中有对应的符号信息(只有添加了它,MSVC才会生成必需的.lib导入库)。同时在使用这个库的项目中引用相关的头文件也需要添加__declspec(dllimport)来导入所需的数据(没有这个扩展的话,在一些场景时会出现无法解析符号的错误,比如静态数据的导出等),所以为了方便使用同一个头文件,在这里采用一个宏来标识这两者的切换时刻。(构建库时导出(__declspec(dllexport)),使用库时导入(__declspec(dllimport)))

    修改CMakeLists文件:

    cmake_minimum_required(VERSION 3.26)
    project(library)
    
    set(CMAKE_CXX_STANDARD 11)
    # SHARED 共享库
    add_library(library SHARED library.cpp)
    # 加入预定义宏 EXPORTTING
    add_definitions(-DEXPORTTING )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在对应环境下采用Cmake工具 构建即可得到对应环境下的动态库文件,比如Windows环境下会生成.lib文件和.dll文件,.lib直接和静态库的配置方式一样,在CMakeLists中修改即可,而将.dll文件直接放置在可执行文件同一路径下,即可隐式链接、调用。而在Linux环境中,只会生成一个.so文件,其在其他项目的cmake使用配置与静态库配置一致。

    前面提到的cmake工具构建,构建涉及的命令可以归纳为以下步骤:

    # 在项目根目录下执行
    
    # 创建build目录 并进行构建
    cmake -Bbuild
    
    # 执行 make 即可完成构建
    # 方式一
    cd build 
    make
    # 方式二 build 是构建目录 lib_test 是构建目标名
    cmake --build build --target lib_test -j 8
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    它们的不同
    • 链接时刻不同:静态库会在编译期完成拷贝与链接,而动态库会在运行时按需载入。
    • 可执行文件大小不同:静态库需要整体进行拷贝成为可执行文件的一部分,而动态库无需拷贝多次(拷贝一次,而后其他程序进行共享),故对于同一个库,采用动态链接会使得体积相比静态库更小。
    • 执行速度不同:静态库在编译期间已经完成链接,而动态库则会有额外的动态载入开销,所以相对来说,静态链接库更快。
    • 库更新的影响不同:静态库因为会成为可执行文件的一部分,所以其更新的时候会导致可执行文件也需要重新编译,而动态库只需更新版本即可(接口不变)。
    参考文章
  • 相关阅读:
    Linux---系统的初步学习【 项目二 管理Linux文件和目录】
    图解LeetCode——1470. 重新排列数组(难度:简单)
    Leetcode - 周赛402
    【死磕JVM】用Arthas排查JVM内存 真爽!我从小用到大
    java执行python时脚本引用动态配置文件遇到的问题
    Socket.D 开源输传协议的集群转发特性
    python判断是否到了文件尾
    centos7 下使用docker安装常见的软件:Redis
    糟糕,数据库异常不可用怎么办?
    求轮廓最大内接圆
  • 原文地址:https://blog.csdn.net/weixin_42792088/article/details/133278198