2022-30: 读完 Effective Modern C++
July 31, 2022

这周靠通勤时间,读完了 Effective Modern C++(EMC),算是对现代 C++ 有了一个基本的认知。这本书一如前三本 Effective 系列的书,语言幽默,讲解清晰,质量上乘,简直是学习现代 C++ 必读的一本书。读完的感受是,这本书得读很多遍,得做为案头参考书随时拿出来回顾。C++ 本身就是一门很复杂的语言,加上各种历史遗留,造成了特别多需要注意的细节,追随 Scott Meyer 的确是一个很好的选择,但如果不在实践中磨练,这些细节会忘的很快。不过好在教育的本质是学完之后没忘记的部分,读过一遍之后,多多少少对内容会有一些印象,在遇到问题的时候,可以意识到在书中似乎有答案。譬如就在前几天,我写了一个抽象类,但没定义虚析构函数,于是运行的时候被 ASAN 抓了个现行,爆了个 new-delete-type-mismatch 的错,搜了一下才意识到这里的问题 Scott 在 Effective C++ 中提到过:Polymorphic base classes should declare virtual destructors. 然后我就瞬间明白了这里的问题所在。

回到现代 C++。总结来说,现代 C++ 最重要的几个特性就是自动类型推导、移动语义(Move Semantices)、智能指针、lambda 函数以及标准并发库,而这五部分恰好就是全书最核心的几个章节。

自动类型推导算是现代语言必备的能力,其意义除了让程序员少敲几个字符,更重要的还在于允许更泛化的代码复用,把原本只属于模版参数的能力,赋予给任何需要类型的地方。

移动语义无疑是最重要的特性,很清晰地把旧 C++ 和新 C++ 区分的清清楚楚,做为追求性能的系统级编程语言,曾经的 C++ 通过指针和引用规避了复制,但随之而来的是各种未定义行为和运行时错误。而移动语义则进一步地减少了复制,并通过新引入的移动构造和移动赋值函数,使得智能指针可以被正确地实现。移动语义本身也引入了新的未定义行为,如操作一个被 move 过的对象就是一个未定义行为,不过现代的编译器通常能抓住这个问题。

智能指针是 C++ 程序员的福音,用好智能指针,基本可以避免资源泄漏和非法内存访问,极大地降低了编程的心理负担。Rust 在这一方面走得更远,可以最大限度地利用编译器来避免错误。EMC 对智能指针的阐述简洁但完备。我曾经并不太理解 weak_ptr 的原理和作用,但读完之后便清晰了——需要访问对象但不对对象的生命周期负责,使用前通过原子操作获取一个 shared_ptr,只有在不为空的时候才能使用。

Lambda 函数算是函数式编程范式的标志性特性,C++ 作为一个多范式编程语言,自然没能免俗。感觉大部分时候 lambda 的用处还是在于和标准库提供的各类便捷的方法结合起来使用,譬如作为比较函数或者 find_if 的参数。使用 lambda 函数最大的坑点在于不小心捕获了局部变量的引用,我就在刚读完这部分的时候被坑了一把,并且仍然是靠 ASAN 发现的——再次说明光看书没有用,还是要实践。

并发库的标准支持是件大喜事,毕竟多核已经是计算机的标配了。C++ 作为性能王者,长期以来都需要通过平台原生的库才能实现并发编程,总算一雪前耻。不过 EMC 的介绍只止步于对并发库本身,C++ 并发编程这个宏大的话题则是超纲内容——计划后续读 C++ Concurrency in Action 来专门补上这个大坑。

Effective 系列最大的特点还是在于对语言的坑做了详细的解释和建议,对于现代 C++ 细节上的改进,如 constexpr、scoped enum、deleted/default/override/const/noexcept 等在定义成员函数时非常有用的关键字,EMC 也做了比较完备的阐述,而这些对于我来说都是可以在工作中用上的。

我的 C++ 学习之路其实非常野。早年间打算法竞赛的时候,基本就是 C with STL 的用法——连 class 都不怎么用,而大学期间则写了很多 C、python 和 Go——系统基础课用的 C,数据类的课用的 python,而但凡和业务扯上关系的代码则是用 Go 来写,因此并没有特别适合使用 C++ 的场合。刚开始工作时也写 Go,而直到今年开始才主力写 C++。我没读过 C++ Accelerated/C++ Primer 这两本据说非常好的 C++ 教材,而是直接上来就读了 Effective C++ 和 Effective Modern C++ 这两本书。我感觉对于已经很熟悉编程这件事情,只是需要立刻能用上 C++ 的我来说,这是一个很高效的方式。毕竟大部头教材虽全,但只是罗列的话并不能迅速让我知道哪些是重点。当然,我的 C++ 之路才刚刚开始,且行且学就好了。

就像前面说的,我接下来会读 C++ Concurrency in Action 这本书,同时也会尝试完成需要用 C++ 的 GAMES101 的作业。此外,我估计还会看一些 Rust 的书,我已经读过了 Programming Rust,接下来会考虑 Jon Gjengset 的 Rust for Rustaceans 和 Tim McNamara 的 Rust in Action。我个人感觉 Rust 比 C++ 其实要好学的多,也更适合个人使用,毕竟工具链更加现代化,也更加容易接触到社区。

这周还看完了 GAMES101 的 shading 第一课,有一半是在讲 Z-buffer,然而感觉很好懂;剩下一半讲了散射光的计算,也算是比较好理解。南大操作系统看了第二课,jyy 的确厉害,程序的状态机视角一出,只有系统调用才能让程序做到计算之外的事的本质地位便呼之欲出了。虽然我甘之如霖,女朋友倒是看的有些吃力。于是 Breaking Bad 推进的特别快,这周靠吃晚饭和睡前的时间,已经把第二季刷完了。

上周五拔掉了最后一颗智齿,伤的比较厉害,缝了线。周末疼了两天,整个人的注意力都被伤口的疼痛牵扯着。张口受限,吞咽困难,舌头一活动舌根就像在拉一辆卡车般地疼。周五晚上不得不吃了止痛药才睡。周日晚上刷牙不小心弄破了伤口,家里并没有无菌棉,只能寄期望于自行凝血。第二天早上醒来,发现满嘴都是血块,枕头上也有一块血印,特别恐怖。不过好在去医院看过并无大碍。直到周六拆线,才感觉能正常吃东西了。

这周还在网上买了一些猫粮,去喂家附近的小野猫。

时候不早了,睡前继续看 jyy,看完再看一集 Breaking Bad 就睡了。