1

c++项目构成及cmake使用基础知识

c++项目构成

  • c++项目代码目录组成

    • 一个良好的代码目录结构如下
      c++目录结构
    • 为何需要注意代码的目录结构

      • 一个良好的代码目录有助于代码的管理以及项目的共享。如果所有代码的头文件以及相应的实现源文件在一个文件夹下面使得项目较为混乱,长时间后项目的创建者自己都难以理清,更不用说别人进行理解。
    • 各组成部分详解

      • 以上图为例,该项目的名称为test2。
      • 项目的头文件放在include文件夹下。
      • 项目的源文件(大部分为头文件的实现)放在src文件夹下。项目的主函数(实现项目的功能)为main.cpp。
      • 项目的最顶层的cmake文件为CMakeList.txt。
      • build文件夹是为了防止cmake执行后产生的文件使得项目的目录变得混乱而创建文件夹。
      • .vscode是vscode所产生的,可以放一些vscode的配置文件(使得vscode进行项目的调试更加方便等作用)。
  • C++编译链接

    • 编译各个cpp文件

      • 一个c++程序的各个部分可以放在不同的cpp文件中,每个cpp文件之间都是相对独立的,只要知道相应声明就能可以进行编译。形成.o文件。
      • 链接使得程序的各个部分相互协作起来完成程序的功能,即将生成的各个.o文件进行链接形成一个可执行文件。
  • c++项目中常见文件及其关系

    • 头文件(.h结尾)

      • 头文件中主要是变量的声明,函数名的声明,类的定义(相关行为的实现一般在源文件中编写,比如内联函数就可在头文件中实现),各种宏定义等。如果我们复用别人的代码,如ncnn中的某些数据结构,某些函数时,需要包含别人的头文件。这样编译器才知道这是什么意思。
      • include是一个预编译命令,将相应头文件的内容复制进相应文件,使得编译器知道这是个啥。
      • include < headfilepatch>这种尖括号形式搜索路径顺序

        1. 编译器设置的头文件查找路径,编译器有默认的查找路径,也可以使用I显示指定
        2. 系统路径
      • include "headfilepatch"这种双引号的形式搜索路径顺序

        1. 当前源文件的所在工作目录
        2. 编译器设置的头文件查找路径,编译器有默认的查找路径,也可以使用I显示指定
        3. 系统路径
      • 两种include的主要注意点

        • 对于名字不冲突的,使用两种均可(标准库文件只要不和自定义文件名字冲突,两种方式都能找到),对于名字冲突的,使用双引号,就会优先使用自定义的,故复用别人代码注意名字冲突的问题。
        • 使用I指定搜索路径的使用,使得搜索路径的可掌握性。
        • 养成良好的include习惯,防止出现所包含的头文件和自己想象的不一样,尤其是自定义的头文件可能存在冲突时源文件(.cpp结尾)
    • 源文件

      • 主要是对函数的实现
    • 库(.so或者.a结尾)

      • 库是别人的可复用代码编译形成目标文件集合,可以将其编译成静态库或者动态库,与一般的编译产生的.o文件一样,最后是要链接到程序中的。
      • 静态库(.a),在运行之前就会将静态库相关目标文件链接到最终的可执行文件中,使得程序运行是不需要依赖库。会产生同样的目标文件在程序多处存在的问题,使得最终可执行文件较大且库的更新较为苦难。
      • 动态库(.so)在运行时才被链接,使得程序运行时依赖库。但其节约内存,且动态库的更新相对容易。

cmake使用基础知识

  • 什么是cmake

    • camke是一个跨平台的编译工具,使得可以使用简单的语法描述一个项目的编译过程(先编译哪些源文件,哪些编译成静态库,哪些编译成动态库,编译目标放在哪里等等)。
  • 为何使用cmake

    • 一个良好的c++程序由很多的源文件组成,当文件数量较少时可以较为方便且快速地手工一个一个分别编译然后链接起来。但当文件数量变多后,每次都手工一个个编译文件就非常繁琐。比如一个c++项目中由两个.cpp文件时,编译命令的参数只用输入两个,此时就算从头分别编译也较为方便,但当项目由上百个.cpp文件构成时,如果还是使用命令进行编译,则会非常繁琐。而使用cmake可以将编译参数写下,使得可重复使用,极大简化编译过程。
    • 且当一个c++项目的编译过程较为复杂时,比如哪些编译为静态库,哪些编译为动态库,编译输出位置及其间的关系是什么样的等,将项目共享给其它人时,被共享者需要知道上述信息,如果项目作者没有使用一种简单的方式提供上述信息,将花费许多不必要的精力在项目编译上。而cmake使得使用简单的语法就可以描述项目的编译过程,使得被共享者知道编译信息且只需要使用make命令就能实现项目的编译,使得项目易于共享。
    • 使用cmake可以使得一个c++项目根据不同平台生成相应的代码版本。同时可以有效控制一个大型项目中某个单元的修改后重新编译、测试等过程,使得大型项目的开发、调试、发布更加高效且便利。
  • 如何使用cmake

    • 使用cmake的大致过程为:编写cmakelists;创建build文件保证项目整洁度;进入build文件夹执行cmake及make命令完成编译得到结果。
    • 一个简单的cmakelists如下
      cmakelists示例
    • 一个经过简单代码分类组织的c++项目编译过程需要知道项目的头文件,源文件,库生成、库链接等信息。上图所示cmake可以有效描述并控制实现该编译过程。

      • cmake_minimum_required(version 版本号) 是指定cmake的最低版本号,每个cmakelists一般都有这条命令。
      • project(项目名称) 是指定项目名称。
      • set(var [value]) set命令用于显示定义变量。图中用于添加编译选项(包括显示警告、生成可调试目标)
      • include_directories() 用于指定项目头文件目录。其中用 ${变量名}的方式使用了变量。CMAKE_SOURCE_DIR的值为最外层cmakelists所在目录。
      • file() 将文件加入到某个变量中,需要具体到文件名。使得可以用一个变量代表多个文件,使得cmake更加简洁。CMAKE_CURRENT_SOURCE_DIR 的值为当前正在处理的cmakelists的目录。
      • message() 用于在编译过程中输出某些信息,以便于更好地掌握编译过程。
      • add_library(库名 库类型 源文件) 用于生成一个库。
      • add_executable(目标名 源文件) 用于编译源文件生成可执行文件。
      • target_link_libraries(可执行文件名 链接方式 库名 ) 用与add_executable命令后,实现可执行文件与动态库的链接。
    • 当编写好cmakelists后,可以在项目最外层目录中创建一个build文件夹以存放编译生成的中间产物以及可执行文件等,以让项目目录更加整洁。
    • 接下来就可以执行命令完成编译得到结果。

      • 先进入到build文件夹中。执行cmake 命令生成makefile。cmake命令需要指定cmakelists所在路径(故一个目录层面中只能有一个cmakelists)。故在build文件中执行cmake .. 因为cmakelists在build文件夹外面。
      • 然后在build中执行make命令执行makefile启动编译并得到结果。make命令需要指定makefile所在路径,由于当前操作makefile就在build文件夹中,故此时只需要执行make命令即可。

xianghanfeng
6 声望1 粉丝