博客 阅读时间 4 分钟

John Carmack:C++ 编程传奇

分享本文
John Carmack: A C++ Programming Legend

您是否看过这样的篮球或足球运动员:他们的比赛方式简单而高效,让人不禁想问——为什么不是每个人都能这样打球?他似乎只用了简单的技术。

作为一名 C++ 程序员,在研究 John Carmack 的源代码时,我有过同样的感受:代码简单到让我们疑惑,为什么我们就不能像他那样写软件。

让我们来探究 Doom 3 源代码中的一些设计选择,试着理解为什么这段代码虽然简单,却非常高效。

2011 年 11 月 23 日,id Software 延续了他们的传统,公布了其上一代引擎的源代码。这份源代码被许多开发者审阅过;下面是 Fabien Sanglard 对 Doom 3 的一段评价(原始出处):

Doom 3 BFG 是用 C++ 编写的,这门语言如此庞大,既能用来写出伟大的代码,也能造出让您“辣眼睛”的怪物。幸运的是,id Software 选择了一个接近“带类的 C”的 C++ 子集,读起来几乎不费脑子:
  • 不用异常。
  • 不用引用(使用指针)。
  • 尽量少用模板。
  • 到处使用 const。
  • 类。
  • 多态。
  • 继承。

总而言之,只使用了 C++98 标准的一个子集。下面是 Doom 3 的一些设计选择:

1. 提供一个带有实用服务的公共基类。

许多类都继承自 idClass:

doom10

idClass 提供以下服务:

  1. 实例创建。
  2. 类型信息管理。
  3. 事件管理。
doom11

2. 让字符串操作变得容易

一般来说,字符串是项目中使用最广泛的类型之一;许多操作都要用到字符串,因此我们需要操作它们的函数。

Doom 3 定义了 idStr 类,它几乎包含了所有操作字符串的实用方法——无需像许多其他框架提供的字符串类那样,再自己定义方法。

3. 源代码与 GUI 框架(MFC)高度解耦

在许多使用 MFC 的项目中,代码与它的类型高度耦合,代码中到处都是 MFC 类型。

在 Doom 3 中,代码与 MFC 高度解耦;只有 GUI 类直接依赖它,如下面的 CQLinq 查询所示:

doom3

这个选择对生产力影响很大:事实上,只有 GUI 开发者需要和 MFC 框架打交道,其他开发者不必在 MFC 上浪费时间。

4. 它提供了一个非常出色的工具库(idlib)

在几乎所有项目中,使用最多的类型都是工具类,如以下查询的结果所示:

doom4

可以看到,使用最多的是工具类。如果 C++ 开发者没有一个好用的工具框架,最终可能会把大量开发时间花在技术层上。

idlib 提供了实用的类和处理字符串、容器与内存所需的全部方法,这让开发者的工作更轻松,让他们能把更多精力集中在游戏逻辑上。

5. 实现非常容易理解

Doom 3 实现了一个硬编码的编译器;正如 C++ 开发者所知,开发解析器和编译器并非易事。然而,Doom 3 的实现非常容易理解,代码也非常整洁。

下面是编译器所使用的类的依赖图:

doom16

下面是编译器源代码的一个片段:

doom15

我们已经研究过许多解析器和编译器的源代码,但这是我们第一次遇到源代码如此容易理解的编译器——整个 Doom 3 源代码都是如此。这简直神奇。研究 Doom 3 源代码时,我们会情不自禁地说:哇,太美了!

总而言之,Doom 3 的源代码非常整洁,易于理解和维护,而且只使用了标准的一个子集。它没有使用任何高深的技术,而是遵循了代码设计、命名和格式方面的基本最佳实践。

可以说,John Carmack 的秘诀就是 KISS 原则,维基百科对其定义如下:

KISS is an 首字母缩写 for "“保持简单,傻瓜”(Keep it simple, stupid)" as a design principle noted by the 美国海军 in 1960.[1][2] The KISS principle states that most systems work best if they are kept simple rather than made complicated; therefore 简单性 should be a key goal in 设计 and unnecessary complexity should be avoided. The phrase has been associated with aircraft engineer Kelly Johnson (1910–1990).[3] The term "KISS principle" was in popular use by 1970.[4] Variations on the phrase include: "Keep it simple, silly", "keep it short and simple", "keep it simple and straightforward",[5] "keep it small and simple" and "keep it stupid, simple".[6]

这个定义中有趣的是下面的论断:

KISS 原则指出:大多数系统在保持简单而非被弄得复杂时,运转得最好。在采用新的 C++ 标准时,我们应该吸取什么教训?

新标准引入了许多有趣的新特性。以为用上所有这些特性就会自动让代码更高效,是个糟糕的想法;许多新特性更适合用于开发通用库,尤其是所有与泛型编程相关的特性。

不要强迫自己使用所有新特性;只有当某个特性确实有必要、并且能让代码更好或更高效时才使用它。例如,这篇有趣的文章就讨论了过度使用 auto 关键字的弊端。

分享本文