自用的规范,参考自Google、华为与微软。
最重要的一致性规则是命名管理,命名风格直接可以直接确定命名实体是:类型、变量、函数、常量、宏等等,无需查找实体声明,我们大脑中的模式匹配引擎依赖于这些命名规则。
命名规则具有一定随意性,但相比按个人喜好命名,一致性更重要,所以不管你怎么想,规则总归是规则。
1. 通用命名规则
1.1 标识符的命名要清晰、明了,有明确含义,同时使用完整的单词或大家基本可以理解的缩写,避免使人产生误解。
说明:尽可能给出描述性名称,不要节约空间,让别人很快理解你的代码更重要。
好的命名示例:
int num_errors; // Good.
int num_completed_connections; // Good.
不好的命名,使用模糊的缩写或随意的字符:
int n; // Bad - meaningless.
int nerr; // Bad - ambiguous abbreviation.
int n_comp_conns; // Bad - ambiguous abbreviation.
1.2 除了常见的通用缩写以外,不使用单词缩写,不得使用汉语拼音。
说明:较短的单词可通过去掉“元音”形成缩写,较长的单词可取单词的头几个字母形成缩写,一些单词有大家公认的缩写,常用单词的缩写必须统一。协议中的单词的缩写与协议保持一致。对于某个系统使用的专用缩写应该在注释或者某处做统一说明。
一些常见可以缩写的例子示例:
全名 | 缩写 |
---|---|
argument | Text |
buffer | buff |
clock | clk |
command | cmd |
compare | cmp |
configuration | cfg |
device | dev |
error | err |
hexadecimal | hex |
increment | inc |
initialize | init |
maximum | max |
message | msg |
minimum | min |
parameter | para |
previous | prev |
register | reg |
semaphore | sem |
statistic | stat |
synchronize | sync |
temp | tmp |
1.3 用正确的反义词组命名具有互斥意义的变量或相反动作的函数等。
示例 | 示例 | 示例 |
---|---|---|
add/remove | begin/end | create/destroy |
insert/delete | first/last | get/release |
increment/decrement | put/get | add/delete |
lock/unlock | open/close | min/max |
old/new | start/stop | next/previous |
source/target | show/hide | send/receive |
source/destination | copy/paste | up/down |
1.4 尽量避免名字中出现数字编号,除非逻辑上的确需要编号。
示例:如下命名,使人产生疑惑。
#define EXAMPLE_0_TEST_
#define EXAMPLE_1_TEST_
应改为有意义的单词命名:
#define EXAMPLE_UNIT_TEST_
#define EXAMPLE_ASSERT_TEST_
1.5 标识符前不应添加模块、项目、产品、部门的名称作为前缀。
说明:很多已有代码中已经习惯在文件名中增加模块名,这种写法类似匈牙利命名法,导致文件名不可读,并且带来带来如下问题:
1)第一眼看到的是模块名,而不是真正的文件功能,阻碍阅读;
2)文件名太长;
3)文件名和模块绑定,不利于维护和移植。若foo.c进行重构后,从a模块挪到b模块,若foo.c中有模块名,则需要将文件名从a_module_foo.c改为b_module_foo.c。
1.6 平台/驱动等适配代码的标识符命名风格保持和平台/驱动一致。
说明:涉及到外购芯片以及配套的驱动,这部分的代码变动(包括为产品做适配的新增代码),应该保持原有的风格。
1.7 重构/修改部分代码时,应保持和原有代码的命名风格一致。
说明:根据源代码现有的风格继续编写代码,有利于保持总体一致。
2.文件命名
因为不同系统对文件名大小写处理会不同(如MS的DOS、Windows系统不区分大小写,但是Linux系统则区分),文件名要全部小写,可以包含下划线(_)或短线(-),按项目约定来。
可接受的文件命名:
my_useful_class.cpp
my-useful-class.cpp
myusefulclass.cpp
C++文件以.cpp结尾,头文件以.h结尾。
不要使用已经存在于/usr/include下的文件名(对 UNIX、Linux 等系统而言), 如 db.h。
通常,尽量让文件名更加明确,http_server_logs.h 就比 logs.h 要好,定义类时文件名一般成对出现,如 foo_bar.h 和 foo_bar.cpp,对应类 FooBar。
内联函数必须放在.h 文件中,如果内联函数比较短,就直接放在.h 中。如果代码比较长,可以放到以-inl.h 结尾的文件中。对于包含大量内联代码的类,可以有三个文件:
url_table.h // The class declaration.
url_table.cc // The class definition.
url_table-inl.h // Inline functions that include lots of code.
3.类型命名
类型命名每个单词以大写字母开头,不包含下划线:MyExcitingClass、MyExcitingEnum。
所有类型命名,包括类、结构体、类型定义(typedef)与枚举都使用相同约定,例如:
// classes and structs
class UrlTable {
class UrlTableTester {
struct UrlTableProperties {
// typedefs
typedef hash_map<UrlTableProperties *, string> PropertiesMap;
// enums
enum UrlTableErrors {
4.变量命名
采用UNIX风格。变量名一律小写,单词间以下划线相连,类的成员变量以下划线结尾,如
my_exciting_local_variable、my_exciting_member_variable_。
使用名词或者形容词+名词方式命名变量。
4.1 普通变量命名
举例:
string table_name; // OK - uses underscore.
string tablename; // OK - all lowercase.
string tableName; // Bad - mixed case.
4.2 类数据成员
结构体的数据成员可以和普通变量一样,不用像类那样接下划线:
struct UrlTableProperties {
string name;
int num_entries;
}
4.3 全局变量与静态变量
全局变量以 g_ 为前缀。
静态变量以s_ 为前缀。
说明:增加g_前缀或者s_前缀,原因如下:
首先,全局变量十分危险,通过前缀使得全局变量更加醒目,促使开发人员对这些变量的使用更加小心。
其次,从根本上说,应当尽量不使用全局变量,增加g_和s_前缀,会使得全局变量的名字显得很丑陋,从而促使开发人员尽量少使用全局变量。
4.4 禁止使用单字节命名变量,但允许定义i、j、k作为局部循环变量。
4.5 禁止使用匈牙利命名法。
说明:变量命名需要说明的是变量的含义,而不是变量的类型。在变量命名前增加类型说明,反而降低了变量的可读性;更麻烦的问题是,如果修改了变量的类型定义,那么所有使用该变量的地方都需要修改。
匈牙利命名法源于微软,然而却被很多人以讹传讹的使用。而现在即使是微软也不再推荐使用匈牙利命名法。历来对匈牙利命名法的一大诟病,就是导致了变量名难以阅读,这和本规范的指导思想也有冲突,所以本规范特意强调,变量命名不得采用匈牙利命名法,而应该想法使变量名为一个有意义的词或词组,方便代码的阅读。
5.常量命名
在名称前加 k,例如:kDaysInAWeek。
所有编译时常量(无论是局部的、全局的还是类中的)和其他变量保持些许区别,k 后接大写字母开头的单词:
const int kDaysInAWeek = 7;
6.函数命名
函数命名应以函数要执行的动作命名,一般采用动词或者动词+名词的结构。
示例:找到当前进程的当前目录
DWORD GetCurrentDirectory( DWORD BufferLength, LPTSTR Buffer );
函数指针除了前缀,其他按照函数的命名规则命名。
普通函数(regular functions,这里与访问函数等特殊函数相对)大小写混合,存取函数(accessors and mutators)则要求与变量名匹配:MyExcitingFunction()、MyExcitingMethod()、my_exciting_member_variable()、set_my_exciting_member_variable()。
6.1 普通函数
函数名以大写字母开头,每个单词首字母大写,没有下划线:
AddTableEntry()
DeleteUrl()
6.2 存取函数
存取函数要与存取的变量名匹配,这儿摘录一个拥有实例变量 num_entries_的类:
class MyClass {
public:
...
int num_entries() const { return num_entries_; }
void set_num_entries(int num_entries) { num_entries_ = num_entries; }
private:
int num_entries_;
};
其他短小的内联函数名也可以使用小写字母,例如,在循环中调用这样的函数甚至都不需要缓存其值,小写命名就可以接受。
7.命名空间
命名空间的名称是全小写的,其命名基于项目名称和目录结构:google_awesome_project。
8.枚举命名
枚举值应全部大写,单词间以下划线相连:MY_EXCITING_ENUM_VALUE。
枚举名称属于类型,因此大小写混合:UrlTableErrors。
enum UrlTableErrors {
OK = 0,
ERROR_OUT_OF_MEMORY,
ERROR_MALFORMED_INPUT,
};
9.宏命名
采用全大写字母,单词之间加下划线“_”的方式命名。例如:
MY_MACRO_THAT_SCARES_SMALL_CHILDREN\par
除了头文件或编译开关等特殊标识定义,宏定义不能使用下划线“_”开头和结尾。
(未完待续)