C++之静态库 vs 动态库

349 阅读3分钟

我将详细为你讲解 C++ 项目中使用 CMake 构建 静态库(Static Library)动态库(Shared Library) 的区别、用法、配置方式以及它们的优缺点。


一、静态库 vs 动态库:基本概念

特性静态库(Static Library)动态库(Shared Library)
文件扩展名.a(Linux/Unix),.lib(Windows).so(Linux),.dll(Windows),.dylib(macOS)
链接时机编译时链接(Link Time)运行时链接(Runtime)
包含方式被复制到最终可执行文件中不复制,运行时动态加载
文件大小可执行文件较大可执行文件较小
内存占用每个程序都有独立副本多个程序可共享一份
更新方式修改库需重新编译整个程序替换库文件即可更新
启动速度快(无需加载外部库)稍慢(需加载库)

二、CMake 中创建静态库和动态库

1. 基本语法

add_library(<name> [STATIC | SHARED | MODULE] <source_files>)
  • STATIC:创建静态库
  • SHARED:创建动态库
  • 不指定类型时,由 BUILD_SHARED_LIBS 变量决定(推荐显式指定)

2. 示例项目结构

project_root/
├── CMakeLists.txt
├── include/
│   └── math_utils.h
├── src/
│   └── math_utils.cpp
└── app/
    ├── main.cpp
    └── CMakeLists.txt

3. 根目录 CMakeLists.txt

# CMake 最低版本
cmake_minimum_required(VERSION 3.10)

# 项目名称
project(MathLibrary LANGUAGES CXX)

# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 添加头文件路径(全局)
include_directories(include)

# 创建静态库
add_library(math_static STATIC
    src/math_utils.cpp
)

# 创建动态库
add_library(math_shared SHARED
    src/math_utils.cpp
)

# 设置目标属性(可选)
set_target_properties(math_static PROPERTIES OUTPUT_NAME "math")
set_target_properties(math_shared PROPERTIES OUTPUT_NAME "math")

# 导出头文件目录
target_include_directories(math_static PUBLIC include)
target_include_directories(math_shared PUBLIC include)

# 添加子目录(应用)
add_subdirectory(app)

4. include/math_utils.h

#ifndef MATH_UTILS_H
#define MATH_UTILS_H

int add(int a, int b);
int multiply(int a, int b);

#endif

5. src/math_utils.cpp

#include "math_utils.h"

int add(int a, int b) {
    return a + b;
}

int multiply(int a, int b) {
    return a * b;
}

6. app/main.cpp

#include <iostream>
#include "math_utils.h"

int main() {
    std::cout << "5 + 3 = " << add(5, 3) << std::endl;
    std::cout << "5 * 3 = " << multiply(5, 3) << std::endl;
    return 0;
}

7. app/CMakeLists.txt

# 创建可执行文件
add_executable(app_main main.cpp)

# 链接静态库
target_link_libraries(app_main math_static)

# 或者链接动态库(二选一)
# target_link_libraries(app_main math_shared)

三、编译与使用

1. 编译项目

mkdir build
cd build
cmake ..
make

生成的文件:

  • libmath.a(静态库)
  • libmath.so(动态库,Linux)
  • app_main(可执行文件)

2. 使用动态库时的注意事项

Linux 下运行时找不到 .so 文件?

解决方法:

  • .so 文件放在系统库路径(如 /usr/lib),或
  • 设置环境变量:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
./app_main

四、何时选择静态库 or 动态库?

场景推荐类型原因
发布独立可执行文件静态库无需依赖外部库
多个程序共享同一功能动态库节省内存,便于更新
希望快速部署静态库一键运行
插件系统动态库(MODULE)运行时加载
第三方库(如 OpenCV)动态库减小体积,避免重复编译

五、高级技巧

1. 使用 BUILD_SHARED_LIBS 控制默认类型

set(BUILD_SHARED_LIBS ON)  # 默认生成 shared 库
add_library(mylib src.cpp) # 自动生成 .so 或 .dll

2. 导出库供其他项目使用

使用 install()export 命令,生成配置文件供他人 find_package()

install(TARGETS math_static math_shared
        LIBRARY DESTINATION lib
        ARCHIVE DESTINATION lib
        INCLUDES DESTINATION include)

install(FILES ${CMAKE_SOURCE_DIR}/include/math_utils.h
        DESTINATION include)

3. 跨平台兼容命名

set_target_properties(math_static PROPERTIES
    OUTPUT_NAME "math"
    PREFIX "lib"
    SUFFIX ".a"
)

六、常见问题

❓ 静态库和动态库能同时存在吗?

✅ 可以!只要名字不同(如 math_staticmath_shared),就可以共存。

❓ 动态库在 Windows 上是 .dll,怎么用?

  • .dll 是运行时库
  • .lib 是导入库(用于链接)
  • CMake 会自动生成

❓ 如何只构建其中一个库?

使用 CMake 选项:

option(BUILD_SHARED "Build shared library" ON)
option(BUILD_STATIC "Build static library" ON)

if(BUILD_SHARED)
    add_library(math_shared SHARED src/math_utils.cpp)
    target_include_directories(math_shared PUBLIC include)
endif()

if(BUILD_STATIC)
    add_library(math_static STATIC src/math_utils.cpp)
    target_include_directories(math_static PUBLIC include)
endif()

调用时指定:

cmake -DBUILD_SHARED=OFF ..

七、总结

类型优点缺点适用场景
静态库独立、启动快体积大、更新难小工具、嵌入式
动态库节省内存、易更新依赖管理复杂大型项目、插件系统