一、SO 库的基本概念
- 动态链接库(SO):在程序运行时被加载,多个程序可共享同一 SO 库,节省内存
- 优点:减小可执行文件体积、便于模块更新(无需重新编译主程序)
- 核心:通过
extern "C"解决 C++ 名称修饰问题,确保库函数能被正确识别
二、创建 SO 库的步骤
1. 准备源文件
假设有一个简单的数学运算模块,包含头文件和实现文件:
math_utils.h(头文件)
#ifndef MATH_UTILS_H
# MATH_UTILS_H
{
;
;
}
C++ 程序打包为 SO 动态链接库可实现代码复用与模块更新。流程包括准备源文件、使用 g++ 编译生成共享对象、通过编译时链接或运行时动态加载(dlfcn.h)调用。需处理 C++ 名称修饰问题,利用 extern "C" 包裹导出函数。高级用法支持带纯虚函数的接口类封装。注意事项涵盖版本兼容性、内存管理及依赖路径配置,适用于大型项目模块化开发。
extern "C" 解决 C++ 名称修饰问题,确保库函数能被正确识别假设有一个简单的数学运算模块,包含头文件和实现文件:
#ifndef MATH_UTILS_H
# MATH_UTILS_H
{
;
;
}
#include "math_utils.h"
// 加法实现
int add(int a, int b) {
return a + b;
}
// 乘法实现
int multiply(int a, int b) {
return a * b;
}
使用 g++ 编译,关键参数:
-fPIC:生成位置无关代码(必选,确保库可被多个程序共享)-shared:指定生成动态链接库-o:指定输出文件名(惯例以 lib 开头,.so 结尾)编译命令:
g++ -fPIC -shared -o libmath_utils.so math_utils.cpp
执行后会生成 libmath_utils.so 文件。
#include <iostream>
#include "math_utils.h"
int main() {
int a = 10, b = 20;
std::cout << "a + b = " << add(a, b) << std::endl;
std::cout << "a * b = " << multiply(a, b) << std::endl;
return 0;
}
编译时需要指定:
-L.表示当前目录)-lmath_utils,省略 lib 前缀和 .so 后缀)编译命令:
g++ main.cpp -o main -L. -lmath_utils
直接运行生成的可执行文件:
./main
可能的错误:
若提示 error while loading shared libraries: libmath_utils.so: cannot open shared object file: No such file or directory,解决方法:
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH/usr/lib 或 /usr/local/lib 目录dlfcn.h)这种方式无需在编译时链接库,可在程序运行中动态加载,适合插件化设计。
#include <iostream>
#include <dlfcn.h> // 动态加载头文件
int main() {
// 加载 SO 库
void* handle = dlopen("./libmath_utils.so", RTLD_LAZY);
if (!handle) {
std::cerr << "加载库失败:" << dlerror() << std::endl;
return 1;
}
// 获取函数指针(注意:函数指针类型必须与实际函数匹配)
typedef int (*AddFunc)(int, int);
AddFunc add = (AddFunc)dlsym(handle, "add");
typedef int (*MultiplyFunc)(int, int);
MultiplyFunc multiply = (MultiplyFunc)dlsym(handle, "multiply");
// 检查函数是否获取成功
const char* error;
if ((error = dlerror()) != NULL) {
std::cerr << "获取函数失败:" << error << std::endl;
dlclose(handle);
return 1;
}
// 调用函数
int a = 10, b = 20;
std::cout << "a + b = " << add(a, b) << std::endl;
std::cout << "a * b = " << multiply(a, b) << std::endl;
// 关闭库
dlclose(handle);
return 0;
}
需要链接动态加载库 -ldl:
g++ dynamic_main.cpp -o dynamic_main -ldl
./dynamic_main
C++ 的类也可以封装到 SO 库中,但需要特殊处理(因为 extern "C" 不支持类)。
#ifndef CALCULATOR_H
#define CALCULATOR_H
class Calculator {
public:
// 纯虚函数(接口)
virtual int add(int a, int b) = 0;
virtual int multiply(int a, int b) = 0;
virtual ~Calculator() {}
};
// 提供 C 风格的创建和销毁函数
extern "C" {
Calculator* create_calculator();
void destroy_calculator(Calculator* calc);
}
#endif // CALCULATOR_H
#include "calculator.h"
class CalculatorImpl : public Calculator {
public:
int add(int a, int b) override {
return a + b;
}
int multiply(int a, int b) override {
return a * b;
}
};
// 实现 C 风格的创建和销毁函数
extern "C" {
Calculator* create_calculator() {
return new CalculatorImpl();
}
void destroy_calculator(Calculator* calc) {
delete calc;
}
}
g++ -fPIC -shared -o libcalculator.so calculator_impl.cpp
#include <iostream>
#include "calculator.h"
int main() {
// 创建对象
Calculator* calc = create_calculator();
// 调用方法
int a = 10, b = 20;
std::cout << "a + b = " << calc->add(a, b) << std::endl;
std::cout << "a * b = " << calc->multiply(a, b) << std::endl;
// 销毁对象
destroy_calculator(calc);
return 0;
}
g++ use_calculator.cpp -o use_calc -L. -lcalculator
extern "C" 包裹导出函数,确保 C 风格的函数名nm -D libxxx.so 查看 SO 库导出的函数列表通过以上步骤,你可以将 C++ 代码封装为 SO 库,并灵活地在其他程序中调用,这在大型项目模块化开发中非常实用。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online