g++制作C++动态库的简洁例程
code review!
文章目录
- g\++制作C++动态库的简洁例程
- 1. 创建 C++ 动态库
- 1.1 动态库源文件
- 1.2 编译动态库
- 2. 使用动态库
- 2.1 命令行编译链接然后运行
- 2.2 使用 CMake 编译链接然后运行
- 3.附加笔记:关于运行时是否能找到libmylib.so的问题汇总
- 3.1.`g++ -L. -Wl,-rpath=. -o main main.cpp -lmylib `详解
- 命令详解
- 1. `g++`
- 2. `-L.`
- 3. `-Wl,-rpath=.`
- 4. `-o main`
- 5. `main.cpp`
- 6. `-lmylib`
- 总结
- 为什么需要这些选项
- 3.2.`g++ -o main main.cpp -lmylib`直接这样可以编译成功吗?
- 具体情况分析
- 1. 默认库路径
- 2. 当前目录下的库文件
- 推荐的命令
- 3.3.`g++ -o main main.cpp -L. -lmylib`直接这样可以编译成功吗?
- 命令详解
- 编译阶段
- 运行阶段
- 解决方法
- 推荐的命令
- 3.4.通过cmake编译,运行时不需要先`export LD_LIBRARY_PATH=.`,直接`./main`可以运行成功吗?
- 主要问题
- 解决方法
- 修改后的 `CMakeLists.txt`
- 步骤
- 解释
- 4.补充笔记:创建一个包含类的C++动态库,并分别使用命令行和CMake进行编译、链接和运行
- 4.1. 创建C++动态库
- 4.1.1 文件结构
- 4.1.2 MyClass.h
- 4.1.3 MyClass.cpp
- 4.1.4 main.cpp
- 4.2. 使用命令行编译和链接
- 4.2.1 编译动态库
- 4.2.2 编译主程序并链接动态库
- 4.2.3 运行主程序
- 4.3. 使用CMake编译和链接
- 4.3.1 创建CMakeLists.txt
- 4.3.2 编译项目
- 4.3.3 运行主程序
下面是一个使用
g++
制作 C++ 动态库的完整简洁例子,并展示了如何通过命令行和 CMake 使用该动态库。
1. 创建 C++ 动态库
1.1 动态库源文件
首先,我们创建一个简单的 C++ 动态库。假设我们有一个库文件 mylib.cpp
:
// mylib.cpp
#include <iostream>void say_hello() {std::cout << "Hello, world!" << std::endl;
}
对应的头文件 mylib.h
:
// mylib.h
#ifndef MYLIB_H
#define MYLIB_Hvoid say_hello();#endif // MYLIB_H
1.2 编译动态库
使用 g++
编译并创建动态库 libmylib.so
:
g++ -shared -fPIC -o libmylib.so mylib.cpp
2. 使用动态库
2.1 命令行编译链接然后运行
首先,创建一个使用该库的应用程序 main.cpp
:
// main.cpp
#include "mylib.h"int main() {say_hello();return 0;
}
编译并链接应用程序:
g++ -L. -Wl,-rpath=. -o main main.cpp -lmylib
运行应用程序:
./main
2.2 使用 CMake 编译链接然后运行
首先,创建一个 CMake 项目文件 CMakeLists.txt
:
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(MyApp)# 添加库的路径
link_directories(${CMAKE_SOURCE_DIR})# 添加头文件路径
include_directories(${CMAKE_SOURCE_DIR})# 添加可执行文件
add_executable(main main.cpp)# 链接动态库
target_link_libraries(main mylib)
然后,在项目目录下创建 main.cpp
:
// main.cpp
#include "mylib.h"int main() {say_hello();return 0;
}
编译并运行应用程序:
# 创建构建目录
mkdir build
cd build# 运行 CMake 配置
cmake ..# 编译项目
make# 运行应用程序
./main
3.附加笔记:关于运行时是否能找到libmylib.so的问题汇总
3.1.g++ -L. -Wl,-rpath=. -o main main.cpp -lmylib
详解
下面是对命令 g++ -L. -Wl,-rpath=. -o main main.cpp -lmylib
的详细解释:
命令详解
g++ -L. -Wl,-rpath=. -o main main.cpp -lmylib
1. g++
g++
是 GNU 编译器集合(GCC)中用于编译 C++ 代码的编译器。
2. -L.
-L
选项指定链接器搜索库文件的目录。.
表示当前目录。这意味着链接器将在当前目录中查找库文件。
3. -Wl,-rpath=.
-Wl
选项将参数传递给链接器(ld
)。-rpath
选项指定运行时库搜索路径。.
表示当前目录。这意味着在运行时,程序会在当前目录中查找动态库。
4. -o main
-o
选项指定输出文件的名称。在这个例子中,输出文件名为 main
,即生成的可执行文件名为 main
。
5. main.cpp
这是源文件的名称,包含 main
函数。在这个例子中,main.cpp
是要编译的源文件。
6. -lmylib
-l
选项用于指定要链接的库。mylib
是库的名称,链接器会寻找名为 libmylib.so
的动态库文件进行链接。
总结
综上所述,这个命令的作用是:
- 使用
g++
编译main.cpp
。 - 在当前目录中搜索库文件(
-L.
)。 - 在生成的可执行文件中指定运行时库搜索路径为当前目录(
-Wl,-rpath=.
)。 - 输出文件名为
main
(-o main
)。 - 链接名为
libmylib.so
的动态库(-lmylib
)。
为什么需要这些选项
-L.
和-lmylib
:确保编译器能够找到并链接libmylib.so
动态库。-Wl,-rpath=.
:确保生成的可执行文件在运行时能够找到并加载libmylib.so
动态库。运行时库路径指定为当前目录,这对于开发和测试环境很有用,但在生产环境中通常会使用更安全和标准的库路径。
通过这些选项,确保了在编译和运行时都能正确找到和使用动态库。
3.2.g++ -o main main.cpp -lmylib
直接这样可以编译成功吗?
g++ -o main main.cpp -lmylib
这个命令在某些情况下可能会编译成功,但通常还需要指定库文件的搜索路径以及运行时库路径,特别是在使用自定义库(如当前目录下的 libmylib.so
)时。
具体情况分析
1. 默认库路径
如果 libmylib.so
位于系统的默认库路径(例如 /usr/lib
或 /usr/local/lib
),那么这个命令可能会成功,因为链接器能够自动找到库文件。
2. 当前目录下的库文件
如果 libmylib.so
位于当前目录,则需要显式告诉链接器在哪里查找库文件,以及在运行时如何找到动态库。为了确保编译和运行成功,建议使用以下更完整的命令:
推荐的命令
g++ -L. -Wl,-rpath=. -o main main.cpp -lmylib
这条命令确保了编译器和运行时都能正确找到库文件。具体解释如下:
-L.
:指定链接器在当前目录查找库文件。-Wl,-rpath=.
:告诉运行时加载器在当前目录查找动态库。-o main
:指定生成的可执行文件名为main
。main.cpp
:要编译的源文件。-lmylib
:指定要链接的库libmylib.so
。
虽然 g++ -o main main.cpp -lmylib
在某些特定情况下可能会成功,但通常需要指定库文件的搜索路径和运行时路径,以确保编译和运行时都能正确找到库文件。推荐使用更完整的命令来避免潜在的问题。
3.3.g++ -o main main.cpp -L. -lmylib
直接这样可以编译成功吗?
g++ -o main main.cpp -L. -lmylib
这个命令可以在编译阶段成功,但在运行时可能会遇到问题,特别是如果 libmylib.so
位于当前目录且未包含在系统默认的库路径中。
命令详解
g++ -o main main.cpp -L. -lmylib
g++
:使用 GNU 编译器编译 C++ 代码。-o main
:指定输出文件的名称为main
。main.cpp
:要编译的源文件。-L.
:指定链接器在当前目录查找库文件。-lmylib
:指定要链接的库libmylib.so
。
编译阶段
在编译阶段,这个命令会成功,因为 -L.
告诉链接器在当前目录查找库文件 libmylib.so
。
运行阶段
在运行阶段,生成的可执行文件需要找到并加载 libmylib.so
。如果 libmylib.so
不在系统默认的库路径中(例如 /usr/lib
或 /usr/local/lib
),运行时会出现类似以下错误:
./main: error while loading shared libraries: libmylib.so: cannot open shared object file: No such file or directory
解决方法
为了在运行时能够找到 libmylib.so
,有几种方法:
-
使用
LD_LIBRARY_PATH
环境变量:在运行程序之前,将当前目录添加到
LD_LIBRARY_PATH
环境变量:export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./main
-
使用
-Wl,-rpath
链接选项:在编译时指定运行时库路径:
g++ -o main main.cpp -L. -lmylib -Wl,-rpath=.
-
将库文件移动到系统默认库路径:
将
libmylib.so
移动到系统默认的库路径,如/usr/lib
或/usr/local/lib
,并运行ldconfig
来更新库缓存。
推荐的命令
为了确保编译和运行都能顺利进行,推荐使用以下命令:
g++ -o main main.cpp -L. -lmylib -Wl,-rpath=.
虽然 g++ -o main main.cpp -L. -lmylib
可以在编译阶段成功,但为了确保在运行时也能找到动态库,建议使用 -Wl,-rpath=.
链接选项,或者在运行时设置 LD_LIBRARY_PATH
环境变量。
3.4.通过cmake编译,运行时不需要先export LD_LIBRARY_PATH=.
,直接./main
可以运行成功吗?
直接使用提供的 CMakeLists.txt
文件,生成的可执行文件 main
在运行时是否需要设置 LD_LIBRARY_PATH
环境变量,取决于系统和 CMake 的配置。
主要问题
默认情况下,CMake 生成的可执行文件并不会自动包含运行时库路径(rpath
)。因此,如果 libmylib.so
不在系统默认的库路径中,运行时会找不到这个库,导致无法运行。
解决方法
为了确保生成的可执行文件可以在运行时找到 libmylib.so
,需要在 CMakeLists.txt 中显式设置 rpath
。
修改后的 CMakeLists.txt
可以修改 CMakeLists.txt
文件来包含运行时库路径信息。以下是修改后的版本:
# CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(MyApp)# 添加库的路径
link_directories(${CMAKE_SOURCE_DIR})# 添加头文件路径
include_directories(${CMAKE_SOURCE_DIR})# 添加可执行文件
add_executable(main main.cpp)# 链接动态库
target_link_libraries(main mylib)# 设置运行时库路径(rpath)
set_target_properties(main PROPERTIESBUILD_RPATH "${CMAKE_SOURCE_DIR}"INSTALL_RPATH "${CMAKE_SOURCE_DIR}"INSTALL_RPATH_USE_LINK_PATH TRUE
)
步骤
-
创建构建目录并进入:
mkdir build cd build
-
运行 CMake 配置:
cmake ..
-
编译项目:
make
-
运行应用程序:
./main
解释
set_target_properties
:为目标main
设置属性。BUILD_RPATH
:设置构建时的rpath
,即在编译生成的可执行文件中包含的库路径。INSTALL_RPATH
:设置安装时的rpath
,对于这个示例,设置与BUILD_RPATH
相同。INSTALL_RPATH_USE_LINK_PATH
:确保安装时使用链接路径。
通过这种方式,生成的可执行文件将包含运行时库路径信息,不需要在运行时设置 LD_LIBRARY_PATH
环境变量,直接运行 ./main
即可找到并加载 libmylib.so
。
通过在 CMakeLists.txt
中设置 rpath
,可以确保生成的可执行文件在运行时能够找到所需的动态库,不需要额外设置 LD_LIBRARY_PATH
环境变量。这种方法对于开发和测试环境非常方便。
4.补充笔记:创建一个包含类的C++动态库,并分别使用命令行和CMake进行编译、链接和运行
4.1. 创建C++动态库
4.1.1 文件结构
├── MyLibrary
│ ├── MyClass.h
│ └── MyClass.cpp
├── main.cpp
├── build (编译输出目录)
4.1.2 MyClass.h
// MyClass.h#ifndef MYCLASS_H
#define MYCLASS_Hclass MyClass {
public:MyClass();void sayHello();
};#endif // MYCLASS_H
4.1.3 MyClass.cpp
// MyClass.cpp#include "MyClass.h"
#include <iostream>MyClass::MyClass() {}void MyClass::sayHello() {std::cout << "Hello from MyClass!" << std::endl;
}
4.1.4 main.cpp
// main.cpp#include "MyClass.h"int main() {MyClass myClass;myClass.sayHello();return 0;
}
4.2. 使用命令行编译和链接
4.2.1 编译动态库
g++ -fPIC -shared -o build/libMyClass.so MyLibrary/MyClass.cpp
4.2.2 编译主程序并链接动态库
g++ -o build/main main.cpp -Lbuild -lMyClass
4.2.3 运行主程序
LD_LIBRARY_PATH=build ./build/main
4.3. 使用CMake编译和链接
4.3.1 创建CMakeLists.txt
在项目根目录下创建 CMakeLists.txt
文件。
cmake_minimum_required(VERSION 3.10)
project(MyProject)# 设置C++版本
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)# 添加库
add_library(MyClass SHARED MyLibrary/MyClass.cpp)# 添加可执行文件
add_executable(main main.cpp)# 链接库
target_link_libraries(main MyClass)
4.3.2 编译项目
mkdir -p build
cd build
cmake ..
make
4.3.3 运行主程序
LD_LIBRARY_PATH=. ./main