跳到正文

把 C++ 学习笔记写成可以运行的东西

C++
写作
为什么每个例子都应该能编译,以及怎样让读者在五分钟内看到结果。
作者

橙照

发布于

2026年10月12日

我以前写 C++ 笔记的方式是:看一段教程,抄一段代码,在旁边写“这里用了引用”。过两周回来,代码编译不过,我也说不清是环境变了,还是当时就抄错了。

后来我把标准改了一点:每个出现在笔记里的例子,都要能单独编译;每个输出,都要是实际运行得到的。 这听起来只是多跑一次编译器,实际上会改变写笔记的方式。

例子要能独立存在

最开始的例子常写成这样:

// 省略头文件和 main
std::vector<int> values{3, 1, 4};
std::sort(values.begin(), values.end());

读者需要自己补 #include、命名空间和 main,还要猜 values 最后是什么。这些“显然”的部分,恰恰是初学者最容易卡住的地方。

现在我会把例子写成完整文件:

#include <algorithm>
#include <iostream>
#include <vector>

int main() {
  std::vector<int> values{3, 1, 4};
  std::sort(values.begin(), values.end());

  for (int value : values) {
    std::cout << value << ' ';
  }
  std::cout << '\n';
}

再附上实际输出:

1 3 4

多出来的几行不是噪音,而是把“你可以直接运行”这件事变成真的。

给每个例子一个位置

如果文章里只有一个例子,贴代码块就够了。但当一章有十几个例子时,散在正文里的代码很快会失去上下文。我的做法是给每篇笔记一个独立目录:

notes/
└── 03-references/
    ├── index.qmd
    ├── 01-basic.cpp
    ├── 02-const-ref.cpp
    └── expected.txt

正文通过代码块引用文件,构建前用一个脚本把源码和输出同步进去。这样例子可以被单独编译,笔记也不会因为复制粘贴而和源码走散。

脚本本身不需要复杂:

for file in notes/*/*.cpp; do
  g++ -std=c++20 -Wall -Wextra -pedantic "$file" -o /tmp/example
  /tmp/example > "${file%.cpp}.out"
done

真正重要的是把它放进发布流程。只要有一次提交没有通过编译,构建就会失败,而不是等到读者发现。

解释“为什么”,而不是复述语法

能运行的例子解决了“对不对”,但没有解决“为什么”。我尽量避免这种句子:

const 表示常量。

它没有错,但读者看完仍然不知道什么时候该用。更有用的写法是先给出一个会出问题的场景,再说明约束:

如果函数参数写成 std::vector<int> values,每次调用都会复制整个数组。数据量大时,这个复制会出现在性能分析里,而不是语法错误里。改成 const std::vector<int>& 可以避免复制,同时承诺函数不会修改它。

前者是定义,后者是判断。笔记的价值通常在后半部分。

一个可以照着做的检查清单

写完一篇 C++ 笔记,我会过一遍:

  • 每个代码块都有完整的 #include 和 main,或者明确说明它只是片段;
  • 代码用 -Wall -Wextra -pedantic 编译过;
  • 输出是实际运行结果,不是手写的“应该输出”;
  • 至少有一个地方解释“不这样写会怎样”;
  • 文中的文件名、命令和目录结构,与示例仓库一致;
  • 读者不需要先读上一篇,也能运行当前例子。

这几条不保证文章写得好,但能保证它不会在读者第一次尝试时就失败。对学习笔记来说,这已经是一个不错的起点。

这些例子会陆续放进 cpp-notes。如果你发现某个例子在你的编译器上跑不通,欢迎写信告诉我编译器和版本。

回到顶部