0.2 如何使用本书
Hi!很高兴你准备正式翻开这本书!在接下来的旅程中,咱们会从第一条 C++ 语句出发,逐渐认识变量、数组、指针、函数、类、模板、STL、并发以及现代 C++ 新特性。内容不少,但不必一看到目录就开始担心。你不需要在第一次阅读时记住每条规则,也不需要在进入下一章前把上一章背得滚瓜烂熟。
学习编程更像学习游泳:看懂动作只是第一步,真正的掌握一定发生在自己下水之后。本书负责把知识拆成能够逐级攀登的台阶,而你需要做的,是亲手运行代码、修改代码、观察结果,并在出错时尝试弄清楚发生了什么。
ABSTRACT
- 零基础读者可以按照章节顺序学习;
- 已有基础的读者可以按主题查阅,并利用章节总结查漏补缺;
- 示例代码应当亲手运行、修改和重新实现;
- 标有标准版本或“选读”的内容,可以根据当前阶段灵活安排;
- 忘记语法时不必硬背,可以使用第 17 章附录快速查询。
0.2.1 先确定自己的阅读方式
不同读者来到这里时,手里的地图并不相同。本书不要求所有人保持完全一样的速度,可以根据自己的基础选择阅读方式。
完全零基础的读者
如果你从未接触过编程,建议从第 0 章开始,按照目录顺序阅读。前几章会解释程序、语句、变量、类型和编译过程,这些看起来基础,却决定了后续能否真正理解函数、类和内存。
第一次阅读时,遇到暂时难以消化的底层细节,可以先留下标记继续前进。只要能够说清楚示例在做什么,并能亲手运行和稍作修改,就已经达到了这一阶段的目标。等后面接触更多实际场景后,再回来阅读,许多概念会自然清楚起来。
接触过其他编程语言的读者
如果你已经会使用 Python、Java、JavaScript 或其他语言,可以较快浏览第 1 至第 3 章,但不要直接跳过类型、对象生命周期、指针和引用。C++ 与许多语言最大的差异,不只是语法,而是对象何时创建和销毁、资源由谁负责、值与引用怎样传递,以及未定义行为可能带来的后果。
建议把更多时间放在第 4、6、7、11、12 和 14 章。这些章节涉及复合类型、函数参数、类、RAII、移动语义、模板和 STL,是建立 C++ 思维方式的重要部分。
已经学过 C 或 C++ 的读者
如果你已经掌握基础语法,可以先阅读目录和各章总结,找出不熟悉的主题,再按需进入正文。第 11 至第 16 章适合系统整理现代资源管理、泛型编程、标准库、并发和新标准知识,第 17 章则可以直接作为日常速查工具。
TIP
跳读不等于只阅读自己已经会的部分。最有效的跳读,是先通过章节总结发现知识缺口,再回到对应小节补齐它。
0.2.2 全书可以分成哪些阶段
虽然全书按照章节连续编排,但学习时可以把它们看成几个阶段。
| 阶段 | 章节 | 主要目标 |
|---|---|---|
| 准备阶段 | 第 0 至第 2 章 | 认识 C++、准备开发环境、理解程序怎样编译运行 |
| 语言基础 | 第 3 至第 6 章 | 掌握类型、复合数据、流程控制和函数 |
| 面向对象 | 第 7 至第 10 章 | 学习类、继承、多态和运算符重载 |
| 资源与泛型 | 第 11 至第 12 章 | 理解内存、RAII、智能指针、移动语义和模板 |
| 标准库与并发 | 第 13 至第 15 章 | 使用 I/O、错误处理、STL、Ranges、线程和协程 |
| 现代 C++ | 第 16 章 | 梳理 C++11 至 C++23,并学习追踪 C++26 和标准草案 |
| 查询与延伸 | 第 17 章 | 速查语法、构建调试、危险行为、术语和学习路线 |
前一阶段通常会为后一阶段准备词汇。例如,不理解引用和对象生命周期,就很难安全使用 Lambda 捕获、视图和并发任务;不了解类和析构函数,也不容易真正理解 RAII 与智能指针。因此,零基础读者不宜为了尽快看到“高级内容”而大段跳过基础章节。
0.2.3 一篇文章应该怎样学习
建议每次只完成一个清楚的小目标,不必用一天时间硬吞下一整章。阅读一篇文章时,可以按照下面的步骤进行。
第一步:先知道这一节解决什么问题
先浏览标题、章节说明和开头几段,弄清楚这一节为什么存在。不要一开始就急着记语法。如果不知道一个工具解决什么问题,即使把写法背下来,也很难在实际程序中想起它。
第二步:亲手输入并运行示例
不要只用眼睛确认“我看懂了”。亲手创建源文件、编译并运行代码。输入代码的过程会暴露许多阅读时容易忽略的细节,例如分号、括号、头文件、命名空间和类型不匹配。
如果本书提供了可直接运行的源代码,也建议先自己输入一个较短版本,再用仓库代码核对。复制能够节省时间,却不能完全替代亲手复现。
第三步:主动修改一个条件
程序成功运行后,至少改动一处:
- 换一组输入数据;
- 改变循环次数或判断条件;
- 增加一个成员或函数参数;
- 把按值传递改成引用,观察行为;
- 故意输入边界值;
- 预测输出后再运行验证。
“修改后为什么仍然正确”与“修改后为什么出错”,都会比重复运行原样代码带来更多收获。
第四步:关掉文章重新写一次
不要求逐字复原,只要能够重新写出核心结构,并解释每一部分的作用。如果卡住,可以回来看一眼,再继续写。需要查阅并不等于没有学会,真正重要的是逐渐减少对完整答案的依赖。
第五步:用自己的话做一个小结
尝试回答三个问题:
- 这一节的新概念是什么?
- 它解决了什么问题?
- 使用时最容易犯什么错误?
如果暂时答不完整,可以带着问题继续学习,再在章节总结处回来检查。
0.2.4 怎样阅读代码示例
阅读代码时,不要只盯着最终输出。可以按程序真正需要的顺序追踪:
- 包含了哪些头文件;
- 定义了哪些类型、函数和变量;
- 对象在什么位置创建;
- 数据通过值、指针还是引用传递;
- 分支和循环在什么条件下执行;
- 函数在何处返回;
- 对象与资源在什么时候销毁。
遇到指针、引用、视图、Lambda 捕获、线程或协程时,再多问一句:被使用的对象现在还活着吗?谁负责保证它继续存在? 这个习惯会帮助你避开大量看似神秘的错误。
示例为了突出当前知识点,通常会省略大型项目中的部分工程结构。它证明的是某项规则或写法,而不一定是可以原封不动搬进所有项目的最终架构。随着章节推进,前面较直接的实现也可能被后面的 RAII、智能指针、算法或现代接口替代。
0.2.5 怎样对待错误
代码编译失败并不意味着学习失败。恰恰相反,编译器正在指出你的理解与语言规则之间还有一处没有对齐。
遇到错误时,建议依次这样处理:
- 从第一条错误开始阅读,后面的错误可能只是连锁反应;
- 查看错误对应的文件和行号,同时检查上一行;
- 核对括号、分号、拼写、声明、类型和头文件;
- 把复杂表达式拆开,把程序缩小到最少代码;
- 打开编译器警告,不要只追求“勉强编译通过”;
- 程序崩溃或结果异常时,使用调试器和 Sanitizer;
- 修复后解释根因,而不只是记住“这样改就行”。
第 17.9 节集中整理了编译、链接和运行时错误,第 17.10 节介绍了调试器、警告与 Sanitizer。遇到问题时,可以先去附录查找固定排查流程。
请注意
不要养成看见错误就整段替换代码的习惯。一次只改一个有理由的地方,并观察错误信息怎样变化,才能知道真正修复了什么。
0.2.6 标准版本标记是什么意思
书中会标注 C++11、C++14、C++17、C++20 或 C++23。这表示某项语言或标准库功能从相应标准开始出现,并不表示标有新版本的内容一定更高级,也不表示旧功能已经不能使用。
例如,范围 for 从 C++11 开始,if constexpr 属于 C++17,Concepts 和 Ranges 属于 C++20,std::print 与 std::expected 属于 C++23。要编译这些示例,需要选择合适的语言模式:
g++ -std=c++20 main.cpp -o app
但开启 -std=c++23 只代表编译器进入对应语言模式,不保证配套标准库已经完整实现所有 C++23 组件。如果找不到 <print>、<expected>、<generator> 等头文件,应先检查编译器和标准库版本。
第 16 章会系统梳理现代 C++ 的演进,第 16.8 节介绍如何阅读提案和标准草案,第 17.13 节则提供标准版本与功能测试宏速查。
0.2.7 “选读”内容应该跳过吗
标为“选读”或明显超出当前阶段的内容,并不是不重要,而是它依赖的知识可能尚未完全出现。第一次阅读时可以先理解它要解决的问题和基本结论,不必立即掌握所有细节。
例如,在基础类型之后提前遇到运算符重载,可以先知道“运算符能够由自定义类型赋予合适行为”,等学完类和函数后再回来深入。C++23、模板元编程、协程和标准草案等内容也可以采用同样策略。
可以使用三种标记管理自己的进度:
已掌握:能够独立写出并解释;能使用:需要查资料,但知道什么时候使用;待回看:目前只知道用途,细节留到后续。
真实开发中,大量知识长期处于“能使用”状态。这很正常,工具书和文档本来就是程序员工作的一部分。
0.2.8 如何使用提示框、脚注和章节总结
书中不同内容承担的作用并不完全一样:
- 章节开头的蓝色提示框列出本章学习目标;
- 普通提示框补充背景、建议或现代写法;
- 警告提示框指出容易导致错误、未定义行为或兼容性问题的内容;
- 脚注解释术语来源、标准版本和不宜打断正文的补充信息;
- 章节总结负责把分散知识重新串成一条主线。
第一次学习时,建议阅读所有警告和脚注。复习时则可以先读章节总结,根据哪些段落无法清楚复述,再返回对应小节。不要把章节总结当成正文替代品,它更像一次知识体检。
0.2.9 第 17 章附录应该怎样使用
附录不是需要从头背到尾的最后一轮考试。它是一只随时可以打开的工具箱:
- 忘记关键字、类型范围或字面量时,查 17.1 和 17.2;
- 需要换算进制或查看 ASCII、Unicode 时,查 17.3 至 17.5;
- 对运算符分组没有把握时,查 17.6;
- 不知道该包含哪个头文件时,查 17.7;
- 需要编译命令或 CMake 模板时,查 17.8;
- 程序报错、崩溃或行为异常时,查 17.9 至 17.11;
- 想整理代码习惯或确认标准支持时,查 17.12 和 17.13;
- 混淆术语或准备继续深入时,查 17.14 和 17.15。
工具书最重要的价值不是让你记住每一页,而是让你知道答案大致在哪一页。
0.2.10 建议的学习节奏
学习时间不必很长,但最好保持连续。一个可行的节奏是:
- 用 20 至 40 分钟阅读一个小节;
- 用同样或更多时间运行和修改示例;
- 第二天不看原文,重新写出核心代码;
- 每完成一章,阅读总结并做一个小程序;
- 每隔几章回顾以前标记为“待回看”的内容。
如果某一节卡住很久,可以先确认它是否是后续内容的必要前提。必要前提应缩小问题、查阅附录或在评论区提问;选读细节则可以做标记后继续前进。学习节奏允许暂停,但不要让一次卡顿变成永久停止。
0.2.11 学会提一个容易解决的问题
当你需要向他人求助时,尽量提供:
- 想实现的目标;
- 实际发生的结果;
- 完整的第一条错误信息;
- 能够复现问题的最小代码;
- 编译器、标准库、操作系统和语言标准版本;
- 已经尝试过哪些排查方法。
相比“为什么不能运行”,这些信息能让别人更快理解问题,也能迫使你在提问前完成一次有价值的自我排查。本书网页版的评论区欢迎讨论,但请不要提交密码、密钥、私人路径和其他敏感信息。
0.2.12 最后给你的几条建议
不要把阅读速度当成学习成果,也不要拿自己和已经写过多年代码的人比较。今天独立解决一个编译错误,弄清一处悬空引用,或者终于明白为什么要使用 const,都是真实的进步。
请允许自己忘记,但不要允许自己在不理解时假装理解;请放心查阅资料,但尽量在得到答案后亲手验证;请勇敢尝试新写法,也要认真尊重编译器警告、对象生命周期和工具链限制。
你不需要等到“准备得非常充分”才开始写程序。读完下一节,创建一个源文件,写下代码,编译它,然后看看会发生什么。许多真正属于你的知识,正是在这一刻开始形成的。