C++——linux下使用gcc或者cmake进行c++程序开发

14 篇文章 1 订阅
10 篇文章 4 订阅

转载自B站课程:基于VSCode和CMake实现C/C++开发 | Linux篇


转载自B站课程:基于VSCode和CMake实现C/C++开发 | Linux篇

GCC文档:https://gcc.gnu.org/onlinedocs/
查参数一样的话,可以去index里查:https://gcc.gnu.org/onlinedocs/gcc-13.2.0/gcc/Option-Index.html
man gcc文档:https://man7.org/linux/man-pages/man1/g++.1.html
G++ Cheatsheet: https://bytes.usc.edu/cs104/wiki/gcc/

1. 开发环境搭建

1.1 GCC编译器,GDB调试器安装

安装GCC,GDB

sudo apt update
# 通过以下命令安装编译器和调试器
sudo apt install build-essential gdb

确认是否安装成功

# 以下命令确认每个软件是否安装成功 # 如果成功,则显示版本号
gcc --version
g++ --version
gdb --version

1.2 CMake安装

安装cmake

# 通过以下命令安装编译器和调试器
sudo apt install cmake

# 确认是否安装成功
# 如果成功,则显示版本号 
cmake --version

2. GCC编译器

  1. GCC 编译器支持编译 Go、Objective-C,Objective-C ++,Fortran,Ada,D 和 BRIG(HSAIL) 等程序;
  2. Linux 开发C/C++ 一定要熟悉 GCC
  3. VSCode是通过调用GCC编译器来实现C/C++的编译工作的(MacOS上的VSCode默认用的是Clang)
  4. 使用 gcc 指令编译 C 代码;使用 g++指令编译 C++ 代码
  5. clang和gcc的参数和使用整体上基本一样,clang是编译C代码,clang++是编译C++代码

2.1 编译过程

  1. 预处理-Pre-Processing,.I文件
    # -E 选项指示编译器只对输入文件进行预处理
    g++ -E test.cpp -o test.i   //.i文件
    
  2. 编译-Compiling,.s文件
    # -S 编译选项告诉 g++ 在为 C++ 代码产生了汇编语言文件后停止编译 
    # g++ 产生的汇编语言文件的缺省扩展名是 .s
    g++ -S test.i -o test.s
    
  3. 汇编-Assembling,.o文件
    # -c 选项告诉 g++ 仅把源代码编译为机器语言的目标代码 
    # 缺省时 g++ 建立的目标代码文件有一个 .o 的扩展名。 
    g++ -c test.s -o test.o
    
  4. 链接-Linking,bin文件(binary二进制的机器码,是可执行文件)
    # -o 编译选项来为将产生的可执行文件用指定的文件名 
    g++ test.o -o test
    

2.2 g++重要编译参数

-g 编译带调试信息的可执行文件

# -g 选项告诉 GCC 产生能被 GNU 调试器GDB使用的调试信息,以调试程序。
# 产生带调试信息的可执行文件
test g++ -g test.cpp

-O[n] 优化源代码

# 所谓优化,例如省略掉代码中从未使用过的变量、直接将常量表达式用结果值代替等等,
# 这些操作 会缩减目标文件所包含的代码量,提高最终生成的可执行文件的运行效率。
# -O 选项告诉 g++ 对源代码进行基本优化。这些优化在大多数情况下都会使程序执行的更快。
# -O2 选项告诉 g++ 产生尽可能小和尽可能快的代码。 如-O2,-O3,-On(n 常为0–3)
# -O 同时减小代码的长度和执行时间,其效果等价于-O1
# -O0 表示不做优化
# -O1 为默认优化
# -O2 除了完成-O1的优化之外,还进行一些额外的调整工作,如指令调整等。 
# -O3 则包括循环展开和其他一些与处理特性相关的优化工作。
# 选项将使编译的速度比使用 -O 时慢, 但通常产生的代码执行速度会更快。
# 使用 -O2优化源代码,并输出可执行文件 
g++ -O2 test.cpp

-l(小写的L) 和 -L 指定库文件 | 指定库文件路径

# -l参数(小写)就是用来指定程序要链接的库,-l参数紧接着就是库名
# 在/lib, /usr/lib和/usr/local/lib(这三个路径是默认搜索路径)里的库,直接用-l参数就能链接
# 链接glog库
g++ -lglog test.cpp
# 如果库文件没放在上面三个目录里,需要使用-L参数(大写)指定库文件所在目录 
# -L参数跟着的是库文件所在的目录名
# 链接mytest库,libmytest.so在/home/bing/mytestlibfolder目录下 
g++ -L/home/bing/mytestlibfolder -lmytest test.cpp

-I(大写的i) 指定头文件搜索目录

# -I
# /usr/include目录一般是不用指定的,gcc知道去那里找,
# 但是如果头文件不在/usr/icnclude 里, 就需要要用-I参数指定了,
# 比如头文件放在/myinclude目录里,那编译命令行就要加上- I/myinclude 参数了,
# 如果不加,会得到一个”xxxx.h: No such file or directory”的错误。
# -I参数可以用相对路径,比如头文件在当前目录,可以用-I.来指定。
# –cflags参数就是用来生成-I参数的。
g++ -I/myinclude test.cpp

-Wall 打印警告信息

# 打印出gcc提供的警告信息 , Wall是Warning All的意思
g++ -Wall test.cpp

-w 关闭警告信息

# 关闭所有警告信息 
g++ -w test.cpp

-std=c++11 设置编译标准

# 使用 c++11 标准编译 test.cpp 
g++ -std=c++11 test.cpp

-o 指定输出文件名

# 指定输出可执行文件名为test 
g++ test.cpp -o test

-D 定义宏

# 在使用gcc/g++编译的时候定义宏
# 例如: -DDEBUG 定义DEBUG宏,可能文件中有DEBUG宏部分的相关信息,用个DDEBUG来选择开启或关闭 DEBUG

示例代码:

// -Dname 定义宏name,默认定义内容为字符串“1” 
#include <stdio.h>
int main() {
#ifdef DEBUG
        printf("DEBUG LOG\n");
    #endif
        printf("in\n");
}
// 1. 在编译的时候,使用gcc -DDEBUG main.cpp 
// 2. #ifdef DEBUG 语句可以被执行

查看帮助手册

> man gcc
或者
> man --help

2.3 示例

有以下源代码文件结构:

.
├── include
│ 	└── Swap.h 
├── main.cpp 
└── src
    └── Swap.cpp
2 directories, 3 files

2.3.1 直接编译

# 将 main.cpp src/Swap.cpp 编译为可执行文件,用I包含头文件的路径
g++ main.cpp src/Swap.cpp -Iinclude -o a.out
# 运行a.out
./a.out

2.3.2 添加额外的参数

# 将 main.cpp src/Swap.cpp 编译为可执行文件 附带一堆参数
# 使用C++11的语言标准,代码优化级别是O2,打印出所有的Warning信息
g++ main.cpp src/Swap.cpp -Iinclude -std=c++11 -O2 -Wall -o b.out # 运行 b.out
./b.out

2.3.3 生成库文件并编译

链接静态库生成可执行文件

## 进入src目录下 $cd src
g++ Swap.cpp -c -I../include  # 默认生成Swap.o的输出
ar rs libSwap.a Swap.o # 生成静态库libSwap.a

参数说明
// -c Only run preprocess, compile, and assemble steps
// man ar 查看帮助文档
//  -r  Replace or add the specified files to the archive.  
If the archive does not exist a new archive file is created.  
Files that replace existing files do not change the order of 
the files within the archive.  New files are appended to the 
archive unless one of the options -a, -b or -i is specified.
// -s  Write an object-file index into the archive, 
or update an existing one, even if no other change is made to the archive.  
You may use this modifier flag either with any operation, or alone.  
Running `ar s' on an archive is equivalent to running `ranlib' on it.


## 回到上级目录 $cd ..
# 链接,生成静态库可执行文件:staticmain
g++ main.cpp -Iinclude -Lsrc -lSwap -o staticmain
// 上一步生成的libSwap.a,在链接的时候可以直接 `-lSwap`
// -Iinclude -Lsrc 分别表示include的目录和src(生成的静态链接库)的目录

  • 静态库文件(.a)的本质是一个归档(archive)

链接动态库(.so)生成可执行文件

## 进入src目录下 $cd src
# 生成动态库libSwap.so
g++ Swap.cpp -I../include -fPIC -shared -o libSwap.so 

// -fPIC 属于Code Generation Options类的参数,
// 如果目标机器支持这个参数,则会生成位置无关的代码,适合用于动态链接和全局偏移表无大小限制的情况,
在AArch64, m68k, PowerPC和SPARC机器上表现有所不同。位置无关的代码需要特殊的支持,因此只适应于特定的机器。
当设置这个参数时,__pic__和__PIC__会被定义为2

// -shared 属于Linker Options类参数
// 生成一个可以和其他目标文件链接得到可执行文件的共享目标文件,并不是所有系统都支持这个参数。
如果支持,则使用时也必须同时指定用于编译的配套参数,比如:-fpic, -fPIC,或者model suboptions

## 上面命令等价于以下两条命令
gcc Swap.cpp -I../include -c -fPIC
gcc -shared -o libSwap.so Swap.o

## 回到上级目录 $cd ..
# 链接,生成动态库可执行文件:sharemain
g++ main.cpp -Iinclude -Lsrc -lSwap -o sharemain

两个其实都是基于.o文件再去生成相应的.a或者.so文件

参考:

2.3.4 运行生成的库文件

# 最终目录结构
.
├── include
│ └── Swap.h
├── main.cpp
├── sharemain
├── src
│   ├── libSwap.a
│   ├── libSwap.so
│   ├── Swap.cpp
│   └── Swap.o
└── staticmain
2 directories, 8 files

运行上面生成的静态链接库和动态链接库

# 运行静态链接库可执行文件  
./staticmain
// 生成的静态链接库是可以直接执行的,因为是直接把.o文件封装了一层

# 运行动态链接库可执行文件
LD_LIBRARY_PATH=src ./sharemain
// 生成的动态链接库不能不指定链接的.o文件路径直接运行,
因为其在执行过程中,会去找链接的.o文件,因此需要指定链接的.o文件的路径

// 关于LD_LIBRARY_PATH这个系统路径设定,一般就是开发人员测试的时候使用,并不建议
作为配置项写到系统路径中去。详见:
https://computing.help.inf.ed.ac.uk/ldlibrarypath
https://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html 
页面内搜索LD_LIBRARY_PATH即可

类似的,如果是Windows,则

  • 单个文件编译生成.obj
  • 多个.obj直接链接得到.exe可执行文件,但是也可以封装多个.obj链接得到.dll或者.lib
  • 从集合角度来看:.exe.dll以及.lib都是多个.obj的集合
  • 使用上,.dll.lib可以在链接时使用,前者不可以直接运行,后者可以直接运行。

3. GDB(GNU Debugger)

3.1 GDB介绍

GDB介绍:

  • GDB是一个用来调试C/C++程序的功能强大的调试器,是Linux系统开发 C/C++最常用的调试器
  • 程序员可以使用GDB来跟踪程序中的错误,从而减少程序员的工作量。
  • Linux 开发C/C++ 一定要熟悉 GDB,
  • VSCode是通过调用GDB调试器来实现C/C++的调试工作的; Windows 系统中,常见的集成开发环境(IDE),如 VS、VC等,它们内部已经嵌套了相应的调试器。

GDB主要功能:

  1. 设置断点(断点可以是条件表达式)
  2. 使程序在指定的代码行上暂停执行,便于观察
  3. 单步执行程序,便于调试
  4. 查看程序中变量值的变化
  5. 动态改变程序的执行环境
  6. 分析崩溃程序产生的core文件

注意:

  1. 编译程序时需要加上-g,之后才能用gdb进行调试:gcc -g main.c -o main
  2. 回车键:重复上一命令

3.2 常用调试命令参数

演示视频详见: 命令行调试

1. gdb [可执行文件名称] 进入调试
> gdb clang_demo  # 执行后,就会有类似python的(base)这样的(gbd)前缀,表明当前进入了gdb的环境
> 
$(gdb)help(h) # 查看命令帮助,具体命令查询在gdb中输入help + 命令

$(gdb)run(r) # 重新开始运行文件(run-text:加载文本文件,run-bin:加载二进制文件)


$(gdb)start # 单步执行,运行程序,停在第一行执行语句

$(gdb)list(l) # 查看原代码(list-n,从第n行开始查看代码。list+ 函数名:查看具体函数)
# 默认是上下各五行

$(gdb)set # 设置变量的值

$(gdb)next(n) # 单步调试(逐过程,函数直接执行)

$(gdb)step(s) # 单步调试(逐语句,跳入自定义函数内部执行)

$(gdb)backtrace(bt) # 查看函数的调用的栈帧和层级关系 

$(gdb)frame(f) # 切换函数的栈帧

$(gdb)info(i) # 查看函数内部局部变量的数值 

$(gdb)finish # 结束当前函数,返回到函数调用点 

$(gdb)continue(c) # 继续运行,相当于vscode里的F5,会执行到下一个断点或者程序结束

$(gdb)print(p) # 打印值及地址

$(gdb)quit(q) # 退出gdb

$(gdb)break+num(b) # 在第num行设置断点 

$(gdb)info breakpoints # 查看当前设置的所有断点 

$(gdb)delete breakpoints num(d) # 删除第num个断点

$(gdb)display # 追踪查看具体变量值

$(gdb)undisplay # 取消追踪观察变量

$(gdb)watch # 被设置观察点的变量发生修改时,打印显示

$(gdb)i watch # 显示观察点 即 info watch

$(gdb)enable breakpoints # 启用断点

$(gdb)disable breakpoints # 禁用断点

$(gdb)x   # 查看内存x/20xw 显示20个单元,16进制,4字节每单元

$(gdb)run argv[1] argv[2] # 调试时命令行传参

$(gdb)set follow-fork-mode child #Makefile项目管理:选择跟踪父子进程(fork())

4. CMake

4.1 CMake说明

  • CMake是一个跨平台安装编译工具,可以用简单的语句来描述所有平台的安装(编译过程)。
  • CMake可以说已经成为大部分C++开源项目标配

图源自:https://cgold.readthedocs.io/en/latest/overview/cmake-can.html

在这里插入图片描述
在没有CMake之前,如果要让一个项目在多个平台下运行,那么需要人工分别在不同平台的项目下修改。下图左:

但是有了CMake之后,CMake会针对不同平台去build对应于特定平台的结果。(CMake里有architecture系统架构这类的参数)。下图右:

即在系统架构层面上做了一层封装,用户只需要去修改CMakeLists.txt,经过CMake就可以适应不同的系统架构。

在这里插入图片描述

4.2 语法特性介绍

基本语法格式:指令(参数 1 参数 2…)

  • 参数使用括弧括起
  • 参数之间使用空格或分号分开

指令是大小写无关的,参数和变量是大小写相关的

变量使用${}方式取值,但是在 IF 控制语句中是直接使用变量名

set(HELLO hello.cpp)
add_executable(hello main.cpp hello.cpp)
ADD_EXECUTABLE(hello main.cpp ${HELLO})

学习资料推荐:

4.3 重要指令和CMake常用变量

4.3.1 重要指令

也可以看看这个-CMake: CMakeLists.txt基本语法及常用

cmake_minimum_required

cmake_minimum_required(VERSION versionNumber [FATAL_ERROR])
指定CMake的最小版本要求

# 例如:CMake最小版本要求为2.8.3
cmake_minimum_required(VERSION2.8.3)

project

project(projectname [CXX] [C] [Java])
定义工程名称,并可指定工程支持的语言

# 例如:指定工程名为HELLOWORLD 
project(HELLOWORLD)

set

set(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
显式的定义变量

# 定义SRC变量,其值为sayhello.cpp hello.cpp 
set(SRC sayhello.cpp hello.cpp)

⭐️include_directories

include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)
向工程添加多个特定的头文件搜索路径 --->相当于指定g++编译器的-I参数

# 将/usr/include/myincludefolder 和 ./include 添加到头文件搜索路径 
include_directories(/usr/include/myincludefolder ./include)

# 或者更常见的
find_package( OpenCV REQUIRED )
include_directories( ${OpenCV_INCLUDE_DIRS} )

⭐️link_directories

link_directories(dir1 dir2 ...)
向工程添加多个特定的库文件搜索路径 --->相当于指定g++编译器的-L参数

# 例如:将/usr/lib/mylibfolder 和 ./lib 添加到库文件搜索路径 
link_directories(/usr/lib/mylibfolder ./lib)

⭐️target_link_libraries

target_link_libraries(target library1<debug | optimized> library2...)
为 target 添加需要链接的共享库 --->相同于指定g++编译器-l参数

# 例如:将hello动态库文件链接到可执行文件main 
target_link_libraries(main hello)

# 或者更常见的
find_package( OpenCV REQUIRED )
target_link_libraries( DisplayImage ${OpenCV_LIBS} )

add_library

add_library(libname [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL]
source1 source2 ... sourceN)
生成库文件 --->相当于执行g++ hello.cpp -fPIC -shared -o hello.so

# 通过变量 SRC 生成 libhello.so 共享库
add_library(hello SHARED ${SRC})

add_compile_options

add_compile_options()
添加编译参数

# 例如:添加编译参数 -Wall -std=c++11 -O2
add_compile_options(-Wall -std=c++11 -O2)

add_executable

add_executable(exename source1 source2 ... sourceN)
生成可执行文件

例如: 编译main.cpp生成可执行文件main 
add_executable(main main.cpp)

add_subdirectory

add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制 存放的位置

# 例如:添加src子目录,src中需有一个CMakeLists.txt 
add_subdirectory(src)

aux_source_directory

aux_source_directory(dir VARIABLE)
发现一个目录下所有的源代码文件并将列表存储在一个变量中,这个指令被用来自动构建临时源文件列表

# 例如:定义SRC变量,其值为当前目录下所有的源代码文件,有点像set设置一个变量,不过更方便
aux_source_directory(. SRC)
# 编译SRC变量所代表的源代码文件,生成main可执行文件 
add_executable(main ${SRC})

4.3.2 常见变量

官网:https://cmake.org/cmake/help/latest/manual/cmake-variables.7.html#cmake-variables-7

CMAKE_C_FLAGS c文件的编译选项
CMAKE_CXX_FLAGS c++文件的编译选项

# 在CMAKE_CXX_FLAGS编译选项后追加-std=c++11,更新CMAKE_CXX_FLAGS的值
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}-std=c++11")

CMAKE_BUILD_TYPE 编译类型(Debug, Release)

# 设定编译类型为debug,调试时需要选择debug 
set(CMAKE_BUILD_TYPE Debug)

# 设定编译类型为release,发布时需要选择release 
set(CMAKE_BUILD_TYPE Release)

CMAKE_C_COMPILER 指定C编译器,比如:gcc或者clang
CMAKE_CXX_COMPILER 指定C++编译器,比如:g++或者clang++

EXECUTABLE_OUTPUT_PATH 可执行文件输出的存放路径
LIBRARY_OUTPUT_PATH 库文件输出的存放路径


变量说明:https://cmake.org/cmake/help/latest/search.html?q=_BINARY_DIR

CMAKE_BINARY_DIR
工程编译发生的目录,即执行cmake命令进行项目配置的目录,一般为build
PROJECT_BINARY_DIR
同CMAKE_BINARY_DIR
<project_name>_BINARY_DIR

  1. 这三个变量指代的内容是一致的
  2. 如果是in-source-build,指的就是工程顶层目录
  3. 如果是 out-of-source 编译,指的是工程编译发生的目录。
  4. PROJECT_BINARY_DIR 跟其他指令稍有区别,不过现在,你可以理解为他们是一致的。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

CMAKE_SOURCE_DIR
工程顶层目录,即入口CMakeLists文件所在路径
PROJECT_SOURCE_DIR
同CMAKE_SOURCE_DIR
<project_name>_SOURCE_DIR

  1. 这三个变量指代的内容是一致的,不论采用何种编译方式,都是工程顶层目录。
  2. 在in-source-build时,与CMAKE_BINARY_DIR 等变量一致。
  3. PROJECT_SOURCE_DIR 跟其他指令稍有区别,现在,你可以理解为他们是一致的。

4.4 CMake编译工程

CMake目录结构:项目主目录存在一个CMakeLists.txt文件

两种方式设置编译规则:

  1. 包含源文件的子文件夹包含CMakeLists.txt文件,主目录的CMakeLists.txt通过add_subdirectory添加子目录即可;
  2. 包含源文件的子文件夹未包含CMakeLists.txt文件,子目录编译规则体现在主目录的 CMakeLists.txt中;

4.4.1 编译流程

在 linux 平台下使用 CMake 构建C/C++工程的流程如下:

  1. 手动编写 CMakeLists.txt。
  2. 执行命令 cmake PATH 生成 Makefile ( PATH 是顶层CMakeLists.txt 所在的目录 )。
  3. 执行命令make 进行编译。

4.4.2 两种构建方式

外部构建(out-of-source build): 推荐使用 ✅
将编译输出文件与源文件放到不同目录中

目录结构
.
├── build_opencv
├── opencv-4.8.0
└── opencv-4.8.0.zip

下载好opencv的压缩包,解压之后,会新建一个build文件夹用于存放构建结果,与源代码不在一个文件夹里。
这就是外部构建。

# 1. 在当前目录下,创建build文件夹 
mkdir build_opencv
# 2. 进入到build_opencv文件夹
cd build_opencv
# 3. 编译上级目录的CMakeLists.txt,生成Makefile和其他文件 
cmake ../opencv-4.8.0
# 4. 执行make命令,生成target
make

内部构建(in-source build): 不推荐使用❌
内部构建会在同级目录下产生一大堆中间文件,这些中间文件并不是我们最终所需要的,和工程源文件放在一起会显得杂乱无章。

# 在当前目录下,编译本目录的CMakeLists.txt,生成Makefile和其他文件 
cmake .
# 执行make命令,生成target
make

4.5 CMake代码实践

4.5.0 VSCode中cmake插件

安装CMake和CMake Tools插件
在这里插入图片描述
一般会自动配置,输入时会出现提示,鼠标放到指令上也会出现文档
在这里插入图片描述
没有的话,要在设置中对CMake Tools进行一下设置,似乎linux会自动设置,Windows不会(可以把CMake的路径加入系统路径中试试)
在这里插入图片描述

4.5.1 最小的CMake工程

例子和官方给的示例基本一致:Step 1: A Basic Starting Point
同时与cmake cookbook给的也类似:1.1 将单个源文件编译为可执行文件
同时作者借用的图源自的那个文档也有类似的:2.4. Minimal example

入门级别的例子,都是差不多的。
只有一个main.cpp文件的时候,文件结构:

.
├── helloworld
├── helloworld.cpp
├── CMakeLists.txt
// CMakeLists.txt
# Set the minimum version of CMake that can be used
cmake_minimum_required(VERSION 3.0)

# Set the project name
project (HELLOWORLD)

# Add an executable
add_executable(helloworld_cmake helloworld.cpp)

上述CMakeLists.txt文件等同于clang++ main.cpp -o hello_cmake

则编译执行的方式是:

cmake . 
// 对当前目录下的CMakeLists.txt进行编译,是in-source-build,生成的内容中有Makefile
make 
// 对当前目录的Makefile文件进行处理,生成`CMakeLists.txt`中指定的helloworld_cmake可执行文件

采用内部构建(in-source-build) 的话,代码的目录结构看起来就会很乱。
在这里插入图片描述

4.5.2 多目录工程 - 直接编译

2.3.4 运行生成的库文件的目录结构为例,看一下多目录结构时CMakeLists.txt的写法

# 最终目录结构
.
├── include
│ └── Swap.h
├── CMakeLists.txt // 主文件夹下的
├── main
├── main.cpp
└── src
    └── swap.cpp 

2 directories, 8 files

所使用的CMakeLists.txt文件:

cmake_minimum_required(VERSION 3.0)
project(SWAP)

# head file path 设置路径时相对于当前CMakeLists.txt所在目录
include_directories( include )

add_executable(main_cmake main.cpp src/swap.cpp )

上述CMakeLists.txt文件等同于clang++ main.cpp ./src/Swap.cpp -Iinclude -o main
编译过程和上面的类似(外部编译要提前创建好文件夹):

mkdir build
cd build
cmake ..
make

更复杂的目录结构的CMakeLists.txt文件的编写可以看看这个:7.7 add_subdirectory的限定范围

5. 综合例子

说明:

  1. 士兵 许三多 有一把枪,叫做 AK47
  2. 士兵 可以 开火
  3. 士兵 可以 给枪装填子弹
  4. 枪 能够 发射 子弹
  5. 枪 能够 装填子弹 —— 增加子弹数量

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值