- 1.构建系统make
- 2.构建系统CMake
- 3.CMake的常见命令行调用
- 4.CMakeLists.txt常见语法
- 5.为什么需要库library?
- 6.CMake 中的静态库与动态库
- 7.为什么 C++ 需要声明
0:46:55 1.构建系统make 2.构建系统CMake
- 为了解决make的以上问题,跨平台的Cmake应运而生
- 只需要写一份CmakeLists.txt,它就能在调用时生成当前系统所支持的构建系统
- CMake可以自动检测源文件和头文件之间的依赖关系,导出到Makefile里面
- CMake具有相对高级的语法,内置的函数能够处理configure,install等常见需求
- CMake可以自动检测当前的编译器,需要添加哪些flag,必须OpenMP,只需要在CMakeLists,txt中指明target_link_libraries(a.out OpenMP::OpenMP_CXX)即可
生成构建文件Makefile 表示输出makefile文件的目录 表示试用clang++作为编译器 cmake -Bbuild -DCMAKE_CXX_COMPILER=clang++ cmake -Bbuild -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_STANDARD=17 构建可执行文件,编译 cd build;make 或者make -C build,优点是:跨平台 或者cmake --build build4.CMakeLists.txt常见语法
add_executable(输出的可执行文件 输入的多个源文件) add_executable(a.out main.cpp hello.cpp) add_library(test STATIC source1.cpp source2.cpp) # 生成静态库 libtest.a add_library(test SHARED source1.cpp source2.cpp) # 生成动态库 libtest.so 创建库以后,要在某个可执行文件中使用该库,只需要: target_link_libraries(myexec PUBLIC test) # 为 myexec 链接刚刚制作的库 libtest.a5.为什么需要库library?
有时候我们会有多个可执行文件,他们之间用到的某些功能是相同的,我们想把这些共用的功能做成一个库,方便大家一起共享.
- 静态库和动态库都是多个.o文件的打包
- 库中的函数可以被可执行文件调用,也可以被其他库文件调用。
- 库文件又分为静态库文件和动态库文件。
- 其中静态库相当于直接把代码插入到生成的可执行文件中,会导致体积变大,但是只需要一个文件即可运行.
- 而动态库则只在生成的可执行文件中生成“插桩”函数,当可执行文件被加载时会读取指定目录中的.dll文件,加载到内存中空闲的位置,并且替换相应的“ 插桩”指向的地址为加载后的地址,这个过程称为重定向。
这样以后函数被调用就会跳转到动态加载的地址去。
- eg:以course/01/05/hello.cpp为例:objdump -D ./build/a.out |less
插装函数puts如下,可跳转到libc.so库中,plt就是插装函数Windows:- 可执行文件同目录,其次是环境变量%PATH%
Linux:
- ELF格式可执行文件的RPATH,其次是/usr/lib等
CMake 除了 add_executable 可以生成可执行文件外,还可以通过 add_library 生成库文件。
add_library 的语法与 add_executable 大致相同,除了他需要指定是动态库还是静态库: add_library(test STATIC source1.cpp source2.cpp) # 生成静态库 libtest.a add_library(test SHARED source1.cpp source2.cpp) # 生成动态库 libtest.so
- 动态库有很多坑,特别是 Windows 环境下,初学者自己创建库时,建议使用静态库。
- 创建库以后,要在某个可执行文件中使用该库,只需要:
target_link_libraries(myexec PUBLIC test) # 为 myexec 链接刚刚制作的库 libtest.a 其中 PUBLIC 的含义稍后会说明(CMake 中有很多这样的大写修饰符)
- eg:course/01/06
动态库必须在可执行文件的同目录或者系统目录下,可执行程序才能运行;
静态库则无所谓,静态库是将实现hello()直接放在a.out中,它不需要动态查找,但是可执行文件会变大
objdump -D ./build/a.out |less
在多文件编译章中,说到了需要在 main.cpp 声明 hello() 才能引用。为什么?
- 因为需要知道函数的参数和返回值类型:这样才能支持重载,隐式类型转换等特性。
例如 show(3),如果声明了 void show(float x),那么编译器知道把 3 转换成 3.0f 才能调用。 - 让编译器知道 hello 这个名字是一个函数,不是一个变量或者类的名字:这样当我写下 hello() 的时候,他知道我是想调用 hello 这个函数,而不是创建一个叫 hello 的类的对象。
其实,C++ 是一种强烈依赖上下文信息的编程语言,
-
举个例子:
vector < MyClass > a; // 声明一个由 MyClass 组成的数组
如果编译器不知道 vector 是个模板类,那他完全可以把 vector 看做一个变量名,把 < 解释为小于号,从而理解成判断‘vector’这个变量的值是否小于‘MyClass’这个变量的值。
正因如此,我们常常可以在 C++ 代码中看见这样的写法:typename decay::type
因为 T 是不确定的,导致编译器无法确定 decay 的 type 是一个类型,还是一个值。
因此用 typename 修饰来让编译器确信这是一个类型名……
-
参考:学C++从Cmake学起,PPT和源码地址



