我将详细为你讲解 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. 基本语法
cmake
add_library(<name> [STATIC | SHARED | MODULE] <source_files>)
STATIC
:创建静态库SHARED
:创建动态库- 不指定类型时,由
BUILD_SHARED_LIBS
变量决定(推荐显式指定)
2. 示例项目结构
css
project_root/
├── CMakeLists.txt
├── include/
│ └── math_utils.h
├── src/
│ └── math_utils.cpp
└── app/
├── main.cpp
└── CMakeLists.txt
3. 根目录 CMakeLists.txt
cmake
# 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
cpp
#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
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
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
cmake
# 创建可执行文件
add_executable(app_main main.cpp)
# 链接静态库
target_link_libraries(app_main math_static)
# 或者链接动态库(二选一)
# target_link_libraries(app_main math_shared)
三、编译与使用
1. 编译项目
bash
mkdir build
cd build
cmake ..
make
生成的文件:
libmath.a
(静态库)libmath.so
(动态库,Linux)app_main
(可执行文件)
2. 使用动态库时的注意事项
Linux 下运行时找不到 .so
文件?
解决方法:
- 将
.so
文件放在系统库路径(如/usr/lib
),或 - 设置环境变量:
bash
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
./app_main
四、何时选择静态库 or 动态库?
场景 | 推荐类型 | 原因 |
---|---|---|
发布独立可执行文件 | 静态库 | 无需依赖外部库 |
多个程序共享同一功能 | 动态库 | 节省内存,便于更新 |
希望快速部署 | 静态库 | 一键运行 |
插件系统 | 动态库(MODULE) | 运行时加载 |
第三方库(如 OpenCV) | 动态库 | 减小体积,避免重复编译 |
五、高级技巧
1. 使用 BUILD_SHARED_LIBS
控制默认类型
cmake
set(BUILD_SHARED_LIBS ON) # 默认生成 shared 库
add_library(mylib src.cpp) # 自动生成 .so 或 .dll
2. 导出库供其他项目使用
使用 install()
和 export
命令,生成配置文件供他人 find_package()
。
cmake
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. 跨平台兼容命名
cmake
set_target_properties(math_static PROPERTIES
OUTPUT_NAME "math"
PREFIX "lib"
SUFFIX ".a"
)
六、常见问题
❓ 静态库和动态库能同时存在吗?
✅ 可以!只要名字不同(如 math_static
和 math_shared
),就可以共存。
❓ 动态库在 Windows 上是 .dll
,怎么用?
.dll
是运行时库.lib
是导入库(用于链接)- CMake 会自动生成
❓ 如何只构建其中一个库?
使用 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()
调用时指定:
bash
cmake -DBUILD_SHARED=OFF ..
七、总结
类型 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
静态库 | 独立、启动快 | 体积大、更新难 | 小工具、嵌入式 |
动态库 | 节省内存、易更新 | 依赖管理复杂 | 大型项目、插件系统 |