4.添加你自己的程序#
参见
本节仅对学习模板的使用进行简单解释. 与学习模板相关的更多内容请参考请参考 对学习模板的解释 和学习模板内的 请读我
文件.
程序#
所谓程序, 就是我们通过编写代码最终生成的, 可供设备运行的东西. 你所打开的 QQ、微信等都是一个独立的程序.
此处说 "添加你自己的程序", 就是要指明你的程序需要叫什么, 由哪些源文件组成. 为此, 你只需要在 CMakeLists.txt 末尾加上一句 add_program(program_name source_file1 source_file2...)
, 就能添加一个名为 program_name
的程序, 它由 source_file1
、 source_file2
等 (所以有个省略号! 不是说你也要输入这个省略号!) 源文件组成. 这些源代码文件内必须有且仅有一个 int main()
函数, 它将作为程序执行的入口.
警告
program_name、source_file1、source_file2 等等都不要有中文、特殊字符或者空格!
program_name 是唯一的, 多个
add_program
不要重复用同一个 program_name. 最好连 Test、test 这样只是大小写不同的情况都别出现.source_file1、source_file2 等源文件必须实际存在于你的电脑上!
备注
负责管理项目结构的 CMake 会在 CMakeLists.txt 保存时自动重新配置, 而我设置了 VSCode 定时自动保存文件, 所以你在修改 CMakeLists.txt 时可能发现配置失败或右下角弹窗报错. 不要紧张! 等整个 add_program
都写好再确定配置是否成功.
当然, 如果你添加了实际不存在的文件作为源文件, CMake 会配置失败并报错.
这样添加之后, 你就可以像之前 运行程序 和 调试程序 那样, 选择它、编译生成它、运行它、调试它.
在 CMakeLists.txt 我添加了以下两个示例:
示例: 单个源文件组成的程序#
1add_program(example_single
2 src/example_single/main.cpp
3)
这段代码添加了一个名为 example_single
的程序, 它由 src/example_single/main.cpp 源代码——src 文件夹里的 example_single 文件夹里的 main.cpp 源文件——组成.
src/example_single/main.cpp 中有一个 int main()
函数, 它就是该程序执行的入口:
1#include "add.hpp"
2
3#include <iostream>
4
5int main() {
6 std::cout << add(5, 3) << '\n';
7}
由源代码可知, 该程序所做的不过是执行 std::cout << add(5, 3) << '\n';
一条语句.
std::cout
std::cout
来自标准库<iostream>
, 我们通过#include <iostream>
来包含 (include) 它.一个程序的代码可能不止一个人在写, 可能你写了一个函数命名为 game, 而别的人也写了一个函数命名为 game. 那么, 如何区分这两个函数呢? C++ 由此引入了名字空间的概念.
std::cout
中的std::
就是表示使用 std 名字空间——标准库所在的名字空间——中的内容, 所以std::cout
就是说使用 std 名字空间中的 cout. 一些教材会在源代码最开始使用using namespace std;
, 这就是将 std 名字空间中的内容引入到全局名字空间, 进而可以省略std::
, 直接使用cout
.c 的意思是字符 (character), out 的意思是输出 (output),
std::cout
的意思即输出字符. 而<<
就像是管道、箭头一样, 将你要输入的内容从右边流向std::cout
, 因此常见的std::cout << "Hello World"
是将右边的字符串流入到cout
中, 即输出"Hello World"
.add(5, 3)
add(5, 3)
函数来自于"add.hpp"
, 我们通过#include "add.hpp"
来包含它.通过 Ctrl-鼠标左键 或 Command⌘-鼠标左键
#include "add.hpp"
的文件名"add.hpp"
部分, 你可以跳转到对应的文件. 可见, 我们跳转到了 include/add.hpp, 这是我配置的公共头文件存放处, 放在此处的头文件将能被任意位置的源文件直接#include
.#include "add.hpp"
该文件是头文件 (header file), 相比于源文件 (source file), 它旨在以
#include
形式被包含到源文件中而被使用, 因此:它不需要被添加到
add_program(<program_name> <source_file1> [source_file2...])
中;为了被正确地
#include
, 它需要编写头文件保护 (见下文).
打开该文件后, 可以看到以下内容:
include/add.hpp#1#ifndef ADD_HPP 2#define ADD_HPP 3 4inline int add(int lhs, int rhs) { 5 return lhs + rhs; 6} 7 8#endif
其中, 开头的
#ifndef
和#define
, 以及末尾的#endif
行被称为头文件保护 (include guard). 头文件往往需要添加头文件保护从而避免重复地添加同一头文件.更多内容请阅读
请读我
文件和你的教材.'\n'
这是换行字符 (newline). 一些教材中会用
std::endl
(endline) 表示换行, 但实际进行了多余操作, 具体请参考 换行符号 和你的教材 (? 真的会讲吗).
示例: 多个源文件组成的程序#
1add_program(example_multiple
2 src/example_multiple/main.cpp
3 src/example_multiple/hello.cpp
4)
这段代码添加了一个名为 example_multiple
的程序, 它由 src/example_multiple/main.cpp 和 src/example_multiple/hello.cpp 中.
1#include "hello.hpp"
2
3int main() {
4 hello();
5}
由源代码可知, 该程序的执行无非是以 int main()
函数为入口, 调用 hello()
函数.
为什么 src/example_multiple/main.cpp 会知道有个 hello()
函数呢? 因为 #include "hello.hpp"
所包含的头文件中书写了该函数的声明, 但是可以注意到的是, 该头文件中并没有定义 hello() 函数要干什么.
1#ifndef HELLO_HPP
2#define HELLO_HPP
3
4void hello(); // 这是 hello 函数, 但是它具体做什么?
5
6#endif
我们的程序是多个源文件的, 另一个源文件 src/example_multiple/hello.cpp 即定义了 hello()
函数:
1#include "hello.hpp"
2
3#include <iostream>
4
5void hello() {
6 std::cout << "hello world!\n";
7}
由此, 该程序的执行就是以 int main()
函数为入口, 调用 hello()
函数, 该函数将会输出 "hello world!\n"
.
回过头来看我们的 add_program
:
1add_program(example_multiple
2 src/example_multiple/main.cpp # 定义程序入口函数 main, 它调用 hello 函数.
3 src/example_multiple/hello.cpp # 定义 hello 函数
4)
那么, 如果我们再写一个源文件 hello2.cpp, 它将 hello()
函数定义为输出 "hello c++!\n"
呢?
1#include "hello.hpp"
2
3#include <iostream>
4
5void hello() {
6 std::cout << "hello c++!\n";
7}
我们自然可以用它和 main.cpp
再组成一个程序:
1add_program(example_multiple
2 src/example_multiple/main.cpp # 定义程序入口函数 main, 它调用 hello 函数.
3 src/example_multiple/hello.cpp # 定义 hello 函数
4)
5add_program(example_multiple2
6 src/example_multiple/main.cpp # 同样用 main.cpp
7 src/example_multiple/hello2.cpp # 用另一种方式定义 hello 函数
8)
这两个程序复用同一个 main.cpp
, 而输出不同的结果.