zl程序教程

您现在的位置是:首页 >  工具

当前栏目

「毁灭战士3」源码就是“保持简洁”的证明

源码 就是 保持 简洁 证明
2023-09-11 14:16:09 时间

假如你在网上搜最好的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”这样的方法。然而,DOOM3从2000开发至2004,没有使用任何现代C++机制。

让我们使用 CppDepend 来看看源代码,探索它的特别之处

DOOM3由少量的几个工程组成,这儿有它的工程列表和一些类型统计。

以及它们之间的依赖关系图:

 

DOOM3定义了很多全局函数。但是,大部分内容实现是在类中。

数据模型使用结构体定义。为了在源代码中对结构体的使用有个更具体的理解,在下图中将它们以蓝色分块显示出来。

在图表中,代码被表示为树形图,树形图表示法能使用嵌套的矩形来表示树状结构。而树结构用来表示代码分层结构。

工程包含命名空间。 命名空间包含类型。 类型包含函数和域(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源代码一样。这太神奇了。当我们探究DOOM3源代码时,我们忍不住会喊:喔,这太漂亮了!

即使DOOM3选择了很基础的设计,但它的设计者所做的决定都是为了开发者能更多的关注游戏逻辑本身,并且为所有技术层面的东西提供便利。这提高了多大的生产力啊。

无论何时使用“带类的C”,你应该明白你自己在干什么。你必须像DOOM3的开发专家一样。但不推荐初学者忽视现代C++建议而冒险。

原文发布时间:2015-01-24

本文来自云栖合作伙伴“linux中国”


【杂(瞎)谈(聊)】易经当中的密码学 看到这个标题,估计可能会有不少读者会有疑问,易经这不是个文学作品吗,怎么和数学相关的密码学给搞到一起了,这不是标题党蛤, 下面小Q来给大家聊聊在易经当中所体现的一些密码学的思想,有些资料来源也不太确定,我凭借记忆进行搜索的,如有错误还请各位读者海涵。
用Python绘制诱人的桑基图,一眼看透熬夜和狗粮的秘密... 桑基图(Sankey diagram),即桑基能量分流图,也叫桑基能量平衡图。它是一种特定类型的流程图,图中延伸的分支的宽度对应数据流量的大小,通常应用于能源、材料成分、金融等数据的可视化分析。因1898年Matthew Henry Phineas Riall Sankey绘制的 蒸汽机的能源效率图 而闻名,此后便以其名字命名为 桑基图 。
与其硬啃“屎山”代码,不如用这六步有条不紊实现代码重构 李慧文 对大规模系统进行重构,如果一个人对着又臭又长的代码硬刚,即使花了大量的时间进行手工验证,最后仍然会有很多问题,特别是一些深路径及特殊场景下的问题。其实,大规模的系统级别重构时是有方法的。我们采访了 Thoughtworks 数字化转型与运营 资深咨询师黄俊彬(QCon+案例研习社讲师),请他来分享 MV*模式重构演进的方法和经验。
1001. 害死⼈不偿命的(3n+1)猜想(15) [模拟] 对任何⼀个⾃然数n,如果它是偶数,那么把它砍掉⼀半;如果它是奇数,那么把(3n+1)砍掉⼀半。这 样⼀直反复砍下去,最后⼀定在某⼀步得到n=1。卡拉兹在1950年的世界数学家⼤会上公布了这个猜 想,传说当时耶鲁⼤学师⽣⻬动员,拼命想证明这个貌似很傻很天真的命题,结果闹得学⽣们⽆⼼学 业,⼀⼼只证(3n+1),以⾄于有⼈说这是⼀个阴谋,卡拉兹是在蓄意延缓美国数学界教学与科研的进
写出一手烂代码的 19 条准则! 「代码写得好」是对机器学习研究者及开发者最好的赞扬。其第一层意思是说,你的模型非常好,有自己的理解与修正;第二层意思是说代码的结构、命名规则、编写逻辑都非常优秀。
只用一行代码就能搞定,博弈论究竟是什么神仙算法? 云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 博弈论是一门很庞大的学科,它算是数学的一个分支,也和运筹学甚至是经济学有关。虽然它严格说起来并不是算法领域的内容,但是有不少关于博弈论有趣的算法和问题。