如果你在网上搜索最好的,C 源代码。「毁灭战士3 | Doom 3」源代码肯定会被多次提到源代码,以证明为什么要说。
我花了一些时间通读 DOOM3 源代码。这可能是我见过的最干净最漂亮的代码。DOOM3是由id Software公司开发、Activision发行的视频游戏。id Software在商业上取得成功,已售出350多万份副本。
在2011年11月23日,id Software保持开源传统,发布他们最后一个引擎的源代码。这个源代码已经被许多开发者审查过了,这里有一个fabien反馈的例子(链接):
DOOM3 BFG是用C 这是一种巨大的语言,它不仅能写出优秀的代码,还能让人讨厌眼睛流血。幸运的是,id Software退而求其次,使用C 子集接近带类C以下规则:
没有异常没有引用(使用指针)少用模板使用常量(Const everywhere)类多态继承
很多C 专家不建议使用带类C这样的方法。DOOM从2000到2004,没有使用任何现代C 机制。
让我们使用 CppDepend 看看源代码,探索它的特点DOOM它由几个工程组成,这里有它的工程列表和一些类型的统计数据。
以及它们之间的依赖图:
DOOM定义了许多全局函数。然而,大多数内容都是在类中实现的。
结构体定义用于数据模型。为了更具体地了解源代码中结构体的使用,在下图中以蓝色块显示。
在图表中,代码表示树形图,嵌套矩形可以用来表示树形结构。树结构用来表示代码分层结构。
该项目包含命名空间。命名空间包括类型。类型包括函数和域。(field)。我们可以观察到它定义了许多结构,例如DoomDLL 40%的类型是结构体。它们被有序地用来定义数据模型。这一实践已被许多项目所接受。该方法最大的缺点之一是多线程应用和结构体public变量并非不可改变的。
支持不可变对象有一个重要原因:并发编程可以显著简化。写一个合格的多线程序是一项艰巨的任务吗?同步线程访问资源(对象或其他)OS资源)。为什么这些操作很难同步?因为很难保证多线程在资源竞争状态下正确读写多个对象。假如没有写操作?换句话说,线程只访问这些对象而不改变?这样就不再需要同步操作了!
让我搜索一个基类:
几乎40%的结构体和类只有一个基类。OOP(面对象编程)使用继承的好处之一是多态,以下蓝色标明源代码中的虚拟函数:
超过30%的函数是虚拟函数,少数是纯虚拟函数。以下是所有虚基类列表:
只有52类被定义为虚基类,其中35类只是纯接口,即这些接口都是纯虚函数。
我们来搜索使用RTTI的函数
只使用了很少的函数RTTI。
确保只使用OOP最基本的概念,不使用先进的设计模式,不过度使用界面和虚拟基础,限制RTTI数据被定义为结构体。
至此这份代码跟很多C 开发者批评的带类C差别不大。
一些有趣的开发者选择帮助我们理解它的奥秘:1-为有用的服务提供公共基础。许多类是从idClass继承:
idClass提供以下服务:
2-字符串操作方便一般来说,字符串是一个项目中使用最多的对象,需要在很多地方使用,需要函数来操作。
DOOM3定义了idstr类,几乎包含所有使用的字符串操作函数,不需要定义函数来接受其他框架提供的字符串。
3-源代码与GUI框架(MFC)高度解耦很多项目都用过MFC之后,它的代码就会和MFC类型高度耦合,可以在代码的任何地方找到MFC类型。
在DOOM3里,代码和MFC只有高度解耦GUI类会直接依赖它。CQLinq查询可以显示这一点:
这种选择对生产力影响很大。事实上,只有GUI只有开发者才会关心MFC其他开发他开发者不应被强制MFC浪费时间。
4-提供了非常好的公共函数库(idlib)几乎所有项目都使用公共工具,如以下查询结果:
正如我们所看到经常使用的就是公共工具类。假如C 如果开发人员不使用良好的公共工具框架,他们将花费大部分时间来解决技术问题。
idlib字符串处理、容器和内存提供了许多有用的类别。它有效地促进了开发者的工作,并使他们更加关注游戏逻辑。
5-很容易理解DOOM3实现了非常困难的编译器,对于C 对于开发者来说,开发语法解析器和编译器并不容易。尽管如此,DOOM3的实现很容易理解,写得很干净。
这里有这些编译类的依赖图:
还有编译器源代码的代码片段:
我们也看过很多语法解析器和编译器的代码,但这是我们第一次发现编译器很容易理解和整个DOOM3源代码是一样的。太神奇了。当我们探索它时DOOM当3源代码时,我们忍不住喊道:哦,太漂亮了!
总结即使DOOM3选择了非常基本的设计,但它的设计师做出的决定是让开发者更加关注游戏逻辑本身,并为所有技术层面的东西提供便利。这提高了生产力。
无论何时使用带类C你应该明白你在做什么。你必须看起来像DOOM3.开发专家是一样的。但不建议初学者忽视现代C 建议冒险。
本文为转载,如需再次转载,请查看 “blog.jobbole.com” 要求。