CMake:将cpp源文件编译为exe文件、
常见的编译器:
动态链接库
-o文件是临时对象文件(预编译),生成了汇编文件
Make软件:只用写出不同文件之间的以来关系,和生成各文件的规则。
>make a.out
·敲下这个命令,就可以构建出a.out这个可执行文件了。
·和直接用一个脚本写出完整的构建过程相比,make指明依賴关系的好处:
1.当更新了cpp时只会重新编译cpp.o,而不需要把其他main.o也重新编译一遍。
2.能够自动并行地发起对he№℃pp和main℃pp的编译,加快编译速度(make-j)。
3.用通配符批量生成构建规则,避免针对每个℃pp和囤重复写g++命令(%,0:%.cpp)。
但坏处也很明显:
1.make在Unix类系统上是通用的,但在Windows则不然。
2.需要准确地指明每个顺目之间的依賴关系,有头文件时特别头疼。
3.make的语法非常简单,不像shell或python可以做很多判断等。
4.不同的编译器有不同的flag规则,为g++准备的爹数可能对MSVC不适用。
CMake:
你或许听过好几种 Make 工具,例如 GNU Make ,QT 的 qmake ,微软的 MS nmake,BSD Make(pmake),Makepp,等等。这些 Make 工具遵循着不同的规范和标准,所执行的 Makefile 格式也千差万别。这样就带来了一个严峻的问题:如果软件想跨平台,必须要保证能够在不同平台编译。而如果使用上面的 Make 工具,就得为每一种标准写一次 Makefile ,这将是一件让人抓狂的工作。CMake 就是针对上面问题所设计的工具:它首先允许开发者编写一种平台无关的 CMakeList.txt 文件来定制整个编译流程,然后再根据目标用户的平台进一步生成所需的本地化 Makefile 和工程文件,如 Unix 的 Makefile 或 Windows 的 Visual Studio 工程。从而做到“Write once, run everywhere”。显然,CMake 是一个比上述几种 make 更高级的编译配置工具。一些使用 CMake 作为项目架构系统的知名开源项目有 VTK、ITK、KDE、OpenCV、OSG 等 [1]。在 linux 平台下使用 CMake 生成 Makefile 并编译的流程如下:
·读取当前目录的CMakeLists.txt,并在build文件夹下生成build/Makefile:
·>cmake -B build
·让make读取 build/Makefile,并开始构建a.out:
·>make -C build
·以下命令和上一个等价,但更跨平台:
·>cmake--build build
·执行生成的a.out:
·>build/a.out
库(Library):
·有时候我们会有多个可执行文件,他们之间用到的某些功能是相同的,我们想把这些共用
的功能做成一个库,方便大家一起共享。
·库中的函数可以被可执行文件调用,也可以被其他库文件调用。
·库文件又分为静态库文件(和单文件作为库来链接类似)和动态库文件。
其中静态库相当于直接把代码插入到生成的可执行文件中,会导致体积变大,但是只需要
一个文件即可运行。(类似于内联)
而动态库则只在生成的可执行文件中生成“插桩”函数,当可执行文件被加载时会读取指
定目录中的.dll文件,加载到内存中空闲的位置,并且替换相应的“插桩”指向的地址为加
载后的地址,这个过程称为重定向。这样以后函数被调用就会跳转到动态加载的地址去。节省可执行文件大小和运行时内存。
lib中存到就是插桩函数
Windows:可执行文件同目录,其次是环境变量%PATH%
·Linux:ELF格式可执行文件的RPATH,其次是/usr/iib等
CMake中的静态库与动态库
·CMake除了add_executable可以生成可执行文件外,还可以通过add_library生成库文件。
.add_libiary的语法与add_executable大致相同,除了他需要指定是动态库还是静态库:
·add_library(test STATC source1.cpp source2.cpp) #生成静态库libtest.a
·add_library(test SHARED source1.cpp source2.cpp) #生成动态库libtest.so
·动态库有很多坑,特别是Windows环境下,初学者自己创建库时,建议使用静态库
·但是他人提供的库,大多是作为动态库的
·创建库以后,要在某个可执行文件中使用该库,只需要:
·target_link_libraries(myexec PUBILC test) #为myexec链接刚刚制作的库libtest.a
动态库需要在运行时在同目录或系统目录找到对应lib文件,才能运行.
头文件:
预处理器:实质将#include替换为头文件中的内容,类似于高级语言中的模块,但因为其全部替换,较为低效。
<>会防止引入当前目录文件(例如一些垃圾文件)
在头文件前加#pragma once,保证改头文件只被引用一次。
CMake中的子模块:
·复杂的工程中,我们需要划分子模块,通常一个库一个目录,比如:
.这里我们把hellolib库的东西移到hellolib文件夹下了,里面的CMakeLists.txt定义了
hellolib的生成规则。
·要在根目录使用他,可以用CMake的add_subdirectory添加子目录,子目录也包含一个
CMakeLists.txt,其中定义的库在add_subdirectory之后就可以在外面使用。
·因为hello、h被移到了hellolib子文件夹里,因此main、cpp里也要改成:
.如果要避免修改代码,我们可以通过target—include—directories指定a.out的头文件搜索目录:(其中第一个hellolib是名,第二个是目录)
·这样甚至可以用<hello.h>来用这个头文件了,因为通过target-include_directories指定的路径被视为与系统路径等价
·但是这样如果另一个b.out也需要用hellolib这个库,难道也得再指定一遍搜索路径吗?
.不需要,其实我们只需要定义hellolib的头文件搜索路径,引用他的可执行文件CMake会
自动添加这个路径:
·这里用了·表示当前路径,因为子目录里的路径是相对路径,类似还有灬表示上一层目录。
·此外,如果不希望让引用hellolib的可执行文件自动添加这个路径,把PUBL℃改成
PRIVATE即可。这就是他们的用途:决定一个属性要不要在被link的时候传播。
第三方库
1.纯头文件库
.有时候我们不满足于C++标准库的功能,免会用到一些第三方库。
.最友好的一类库莫过于纯头文件库了,这里是一些好用的header-only库:
1.nothings/stb.大名鼎鼎的stb_image系列,涵盖图像,声音,字体箐,只需单头文件!
2.Neargye/magic—enum-枚举类型的皮射,如枚举转字符串箐(实现方式很巧妙)
3.g-truc/glm-仿GLSL语法的数学矢量/矩阵库(附带一些常用函数,随机数生成箐)
4.Tencent/rapidjson-单纯的JSON库,甚至没依賴STL(可定制性高,工程美学经典)
5.ericniebler/range-v3-C++20ranges库就是受到他启发(完全是头文件组成)
6.fmtlib/fmt-相式化库,提供std::format的替代品(需要-DFMT_HEADER_ONLY)
7.gabime/spdlog-能适配控制台,安卓等多后端的日志库(和fmt冲突!)
只需要把他们的include目录或头文件下载下来,然后include_directories(spdlog/include)即可。
·缺点:函数直接实现在头文件里,没有提前编译,从而需要重复编译同样内容,编译时间长。
valarray C++98就引入了标准库,可以类似于numpy
glm数学库:很好用的库
2.作为子模块引入
·第二友好的方式则是作为CMake子模块引入,也就是通过add_subdirectoryø
·方法就是把那个I页目(以fmt为例)的源码放到你工程的根目录:
·这些库能够很好地支持作为子模块引入:
1.fmtlib/fmt-格式化库,提供std::format的替代品
2.gabime/spdlog-能适配控制台,安卓等多后端的日志库
3.ericniebler/range-v3,C++20ranges厍就是受到他启发
4.g-truc/glm-仿GLSL语法的数学矢量/矩阵库
5.abseil/abseil-cpp -旨在补充标准库没有的常用功能
6.bombela/backward-cpp-实现了C++的堆栈回溯便于调试
7.google,/googletest-谷单元测试框架
8.google/benchmark·谷歌性能评估框架
9.glfw/glfw OpenGL窗口和上下文管理
10.libigl/libigl 各种图形学算法大合集
3.引入系统中预安装的第三方库
包管理器:windows无自带的包管理器,可以下载vcpck
现代CMake认为一个包可以提供多个库,又称组件
以下是bilibili课程视频: