“这是我见过的最整洁、最优美的代码!Doom 3的源代码让我对那些优秀的程序员刮目相看。” —– Shawn McGrathz
虽然自己的在技术上的造诣可能一辈子达不到约翰·卡马克的造诣,但是也曾欣赏过《DOOM3》的代码,整洁,优美,希望自己的代码,在美观、易读性上能有所提高,于是阅读了《代码之美——Doom3源代码赏析》和《约翰卡马克的编码规范》后,将《约翰卡马克的编码规范》转载至此。
通用命名规则
tab 键的空格数设置为4个。
在每处都使用大括号(if,else,函数,结构体,typedef,类定义,等等)
1 | if ( x ) { |
else 代码段的左大括号 与 右大括号开始在同一行
1 | if ( x ) { |
表达式与左右括号间要用空格空开。
例如:用
1 | if ( x ) { |
代替如下语句
1 | if (x) { |
又如,用
1 | x = ( y * 0.5f ); |
代替如下语句
1 | x = (y * 0.5f); |
为浮点值添加精度标示符,除非明确需要用到double
例如:用
1 | float f = 0.5f; |
代替如下语句
1 | float f = 0.5; |
又如,用
1 | float f = 1.0f; |
代替如下语句
1 | float f = 1.f; |
函数名以大写字母开头:
例如:
1 | void Function( void ); |
若为多单词函数名,每个词以大写字母开头:
例如:
1 | void ThisFunctionDoesSomething( void); |
函数的标准头部注释如下:
1 | /* |
变量名以小写字母开头。
1 | float x; |
若为多单词变量名,则开头单词以小写字母开头并且后续的每个单词都以大写字母开头。
1 | float maxDistanceFromPlane; |
类型定义名的大小写规则与变量名命名规则相同,但类型定义名要以”_t”结尾。
1 | typedef int fileHandle_t; |
结构体的命名规则与变量名命名规则相同,但要以”_t”结尾。
1 | struct renderEntity_t; |
枚举的命名规则与变量名命名规则相同,但要以”_t”结尾。枚举常量全部都用大写字母。多单词的枚举常量名要以下划线隔开。
例如:
1 | enum contact_t { |
递归函数的名称结尾要加”_r”
例如:
1 | void WalkBSP_r( int node ); |
宏定义名都用大写字母。若为多个单词则单词之间用下划线隔开。
1 |
尽可能的应用’const’限定符
要用:
1 | const int *p; // pointer to const int |
不要用:
1 | int const *p; |
类命名规则
标准的类注释开头如下:
1 | /* |
类名称以”id”起首且每个后续单词以大写字母开头。
1 | class idVec3; |
类成员变量名的命名规则与一般变量名命名规则相同。
例如:
1 | class idVec3 { |
类成员函数的命名规则与一般函数的命名规则相同。
1 | class idVec3 { |
对类成员变量与类成员函数名进行缩进以使其有更好的列排布。成员变量类型与成员函数类型放在第一列,成员变量名与成员函数名放在第二列。
1 | class idVec3 { |
考虑到*符号也是类型的一部分,所以将指针的 * 符号也放在第一列,以增加可读性。
排布类成员变量与类成员函数应该按如下方式排列。
友元类的声明列表 list of friend classes
公共成员变量的声明 public variables
公共成员函数的声明 public methods
受保护成员变量的声明 protected variables
受保护成员函数的声明 protected methods
私有成员变量的声明 private variables
私有成员函数的声明 private methods
这样的声明顺序可以让人在类声明的开头更容易找到公共接口。
当类成员函数不更改类成员变量时永远将该成员函数声明为’const’的。
避免使用”const_cast”。当对象需要被更改但又仅有const的访问权限时,需要定义一个明确给出一个可更改版的对象。这样可以将’const’的权限完全掌握在对象手中而不是用户。
返回’const’对象除非对已该对象的用法是改变其状态。例如,媒介对象如idDecl 应该大部分在代码中都是const的。但idEntity对象的状态往往会被多变的系统更改,则可以让其声明为非const的。
在大部分时间中要尽量避免函数的重载。
例如,不要用:
1 | const idAnim * GetAnim(int index ) const; |
要用如下方式代替:
1 | constidAnim * GetAnimByIndex( int index ) const; |
明确的函数命名往往可以减少程序员出错与非故意的将错误的数据类型传参调用函数的机会。
例如:
1 | Anim = GetAnim( 0 ); |
该调用的目的可能是调用该函数以获得一个随机的动画,但编译器可能将其解释为获得一个在0索引处的动画。
若重载函数的目的是为了增加一个’const’访问权限的函数式容许的。
1 | class idAnimatedEntity : publicidEntity { |
在这个例子中,const版本的GetAnimator提供用来容许GetAnimator在const函数中可以被调用。因为idAnimatedEntity通常是一个非const对象,这样的重载是可以被容许的。对于通常情况是const得媒介类型,函数的重载应该避免:
例如:
1 | class idDeclMD5 : public idDecl { |
id编辑器的命名规则
1 | id<name>Dlg // 对话框类 dialogclass |
文件命名规则
每一个类应该有单独的源文件除非将一些小一点的类聚合为一组是有理由的。
文件名应该与类型相同只是去掉”id”前缀。
例如:
1 | class idWinding; |
当一个类需要跨过多个文件时,这些文件要以类名起首去掉”id”,后面跟着一个下划线和一个子名称。
例如:
1 | class idRenderWorld; |
当一个类是一个公有虚接口对于子系统,公有接口在以类名去掉”id”前缀的的头文件中被实现。实现子系统的类被放在以类名开头但去掉”id”前缀且以”_local.h”结尾的头文件中定义。该类的实现应放在类名开头去掉”id”前缀的cpp文件中实现。
例如:
1 | class idRenderWorld; |
关于约翰·卡马克
如果必须有偶像的话,我的偶像就是—John Carmack, id software
本文来自 hellspawn 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/outsp66336/article/details/8705365?utm_source=copy