C++ 系列前言:从语法到工程能力
1. 为什么重新整理 C++
C++ 不是一门只靠记语法就能写好的语言。
写出 for、class、vector 并不难,真正容易出问题的是这些代码背后的运行机制:对象什么时候创建和销毁,指针保存的地址是否仍然有效,拷贝发生在什么地方,容器扩容会不会让迭代器失效,编译器为什么能通过声明找到定义。
这些问题平时看起来分散,但在实际工程里经常同时出现。一个程序崩溃,可能不是算法写错了,而是访问了已经失效的对象;一个链接错误,可能不是代码逻辑问题,而是声明、定义和编译单元之间的关系没有处理好。
所以这个系列的重点不是把 C++ 语法列表重新讲一遍,而是把常见概念放回程序运行和工程开发的上下文中理解。
2. 这个系列想解决什么问题
这个系列的目标,是把“能写一点 C++ 代码”逐步推进到“能解释代码行为,并写出更可靠的 C++ 代码”。
我会重点关注几个方向:
- 类型与对象:变量、对象、地址、生命周期、值类别等基础概念
- 指针与引用:内存访问、解引用、空指针、悬空指针、函数参数传递
- 资源管理:构造析构、拷贝控制、移动语义、RAII、智能指针
- STL 使用:容器、迭代器、算法、扩容、失效规则和性能取舍
- 工程工具:头文件、源文件、编译链接、CMake、gdb、库文件
- 小项目实践:把零散概念放到一个可运行、可调试、可维护的项目里验证
这不是一套“高手教程”,更像是一份逐步补全 C++ 基本功的技术笔记。
3. 写作方式
每篇文章会尽量采用同一套结构:
- 先说明要解决的具体问题
- 给出一段足够小的 C++ 示例
- 展示可能的运行结果或编译错误
- 解释现象背后的语言规则
- 总结容易踩坑的地方
- 给出当前阶段可以记住的判断规则
这样写的原因很简单:C++ 里的很多概念只看定义很容易产生错觉,必须通过代码、输出和错误信息反复验证。
例如“指针保存地址”这句话很短,但真正写代码时还会继续遇到:
- 指针本身也有地址
- 指针类型会影响解引用方式
- 指针加法不是普通整数加法
- 空指针和悬空指针都不能解引用
- 函数拿到指针后可能修改调用者的对象
把这些细节拆开验证,理解会更稳。
4. 暂定目录
目录会随着学习进度调整,但整体路线先按“基础概念 -> 对象与内存 -> STL -> 工程工具 -> 小项目实践”推进。
第一部分:C++ 基础概念
- 变量、地址和指针
- 指针和引用的差异
const到底修饰谁- 栈、堆和对象生命周期
new/delete和malloc/free的区别
第二部分:对象与内存管理
- 构造函数和析构函数什么时候执行
- 浅拷贝和深拷贝为什么危险
- 拷贝构造函数和赋值运算符有什么区别
- 为什么基类析构函数有时要写成
virtual this指针到底是什么
第三部分:STL 与常用容器
vector为什么会自动扩容vector和list应该怎么选map和unordered_map的区别- 迭代器失效是什么问题
push_back和emplace_back的区别
第四部分:编译、调试与工程工具
- C++ 项目为什么要分
.h和.cpp - 一个 C++ 程序从
.cpp到可执行文件经历了什么 - 使用 CMake 管理一个最小 C++ 项目
- 用 gdb 定位一次段错误
- 静态库和动态库的区别
第五部分:小项目实践
- 用 C++ 写一个命令行小工具
- 从零写一个简单日志系统
- 线程池解决了什么问题
- 从零写一个最小线程池
- 一个 C++ 小项目的完整复盘
5. 需要先建立的几个判断
后面的文章会反复围绕几个判断展开。
第一,C++ 代码是否正确,不能只看“能不能编译过”。编译通过只能说明语法和类型检查没有发现问题,不代表运行时访问的内存一定有效。
第二,资源的所有权必须说清楚。谁创建对象,谁负责释放资源,谁只是临时观察对象,这些关系如果不明确,后面就很容易出现泄漏、重复释放或悬空访问。
第三,工具链也是 C++ 学习的一部分。编译错误、链接错误、运行时崩溃和调试信息,都是理解程序行为的重要入口。
第四,C++ 的复杂性需要用约束来管理。能用标准库就不要手写底层结构;能用 RAII 就不要手动管理释放;能让生命周期清晰,就不要把裸指针到处传。
6. 从哪里开始
这个系列从指针开始。
指针是 C++ 里最基础也最容易误解的概念之一。它连接了变量、地址、内存访问、数组、动态分配、函数参数、对象生命周期等很多内容。
理解指针不等于以后处处使用裸指针,但如果完全绕开指针,就很难真正理解 C++ 为什么会有空指针、悬空指针、迭代器失效、智能指针和 RAII。
后面的文章会从最小示例开始,一步一步把这些概念补起来。