一、命名约定
最重要的一致性规则是命名管理。命名的风格能让我们在不需要去查找类型声明的条件下快速地了解某个名字代表的含义:类型,变量,函数,常量,宏等等,甚至我们大脑中的模式匹配引擎非常依赖这些命名规则。
命名规则具有一定随意性,但相比按个人喜好命名,一致性更重要,所以无论你认为它们是否重要,规则总归是规则。
二、通用命名规则
2.1 总述
函数命名,变量命名,文件命名要有描述性;少用缩写
。
以达到“望文生义”
,看其名,知其意。
2.2 说明
尽可能使用描述性的命名,别心疼空间,毕竟相比之下让代码易于新读者理解更重要。不要用只有项目开发者能理解的缩写,也不要通过砍掉几个字母来缩写单词。
规范命名:
int price_count_reader; // 无缩写
int num_errors; // "num" 是一个常见的写法
int num_dns_connections; // 人人都知道 "DNS" 是什么
不规范命名:
int n; // 毫无意义
int nerr; // 含糊不清的缩写
int n_comp_conns; // 含糊不清的缩写
int wgc_connections; // 只有贵团队知道是什么意思
int pc_reader; // "pc" 有太多可能的解释了
int cstmr_id; // 删减了若干字母
注意,一些特定的广为人知的缩写是允许的,例如用 i
表示迭代变量和用 T
表示模板参数。
三、命名体系
本文推荐3种命名体系,一个项目中只能选用一种,不得混用。
3.1 UNIX like体系
单词用小写字母,每个单词用下划线 ”_”
分割。
举例:
int number_of_bytes;
int success_flag;
int return_value;
3.2 Small-camel-case体系
小驼峰式命名,除第一个单词小写
之外,其他单词首字母大写
。
举例:
int numberOfBytes;
int successFlag;
int returnValue;
3.3 Big-camel-case体系
大驼峰式命名,每个单词首字母大写
。
举例:
int NumberOfBytes;
int SuccessFlag;
int ReturnValue;
四、文件命名
4.1 总述
文件名要全部小写
,可以包含下划线 “_”
或连字符 “-”
, 依照项目的约定。如果没有约定,那么 “_”
更好。
4.2 说明
可接受的文件命名示例:
my_useful_class.cc
my-useful-class.cc
myusefulclass.cc
-
myusefulclass_test.cc
//_unittest
和_regtest
已弃用
原则上,一个 .c
文件须有一个名称相同的 .h
文件相对应。
C++ 文件要以 .cc
结尾,头文件以 .h
结尾,定义类时文件名一般成对出现, 如 foo_bar.h
和 foo_bar.cc
,对应于类 FooBar
。
内联函数必须放在 .h
文件中。如果内联函数比较短,就直接放在 .h
中。
通常应尽量让文件名更加明确。http_server_logs.h
就比 logs.h
要好。
五、类型命名
5.1 总述
类型名称的每个单词首字母均大写,不包含下划线: MyExcitingClass
,MyExcitingEnum
5.2 说明
所有类型命名——类,结构体,类型定义(typedef
),枚举,类型模板参数——均使用相同约定,即以大写字母
开始,每个单词首字母均大写
,不包含下划线。
类名和对象名应该是名词或名词短语。
举例:
// 类和结构体
class UrlTable { ...
class UrlTableTester { ...
struct UrlTableProperties { ...
// 类型定义
typedef hash_map<UrlTableProperties_t *, string> PropertiesMap;
// using 别名
using PropertiesMap = hash_map<UrlTableProperties_t *, string>;
// 枚举
enum UrlTableErrors { ...
5.3 类型定义命名
/*
** 基本数据类型的重定义,小写比首字母大写更有利于延长Shift寿命,
** 但也更容易产生命名冲突。
*/
typedef unsigned char byte;
typedef unsigned char byte_t;
typedef unsigned char Byte;
typedef unsigned char Byte_t;
用 typedef
定义结构体别名时,首字母大写
并加后缀 “_t”
举例:
typedef struct
{
string name;
int num_entries;
static Pool<UrlTableProperties>* pool;
} UrlTableProperties_t;
5.4 类命名
定义一个类以 C
做为类名前缀。
class CMyListCtrl;
六、变量命名
6.1 普通变量命名
- 变量(包括函数参数)和数据成员名开头字母一律
小写
,单词之间可用下划线连接。 - 建议采用
Small-camel-case体系
举例:
string table_name;
string tableName;
char* pTableName;
6.2 静态变量命名
静态变量增加 s_
作为前缀。
举例:
int s_count;
int s_returnValue;
6.3 全局变量命名
全局变量增加 g_
作为前缀。
举例:
int g_currentFile;
int g_username;
6.4 类数据成员命名
不管是静态的还是非静态的,类数据成员都可以和普通变量一样,但要接下划线。
class TableInfo
{
...
private:
string table_name_; // 好 - 后加下划线
string tablename_; // 好
static Pool<TableInfo>* pool_; // 好
};
或者成员变量前加 m_
作为前缀。
举例:
string m_tableName;
6.5 结构体变量命名
不管是静态的还是非静态的,结构体数据成员都可以和普通变量一样,不用像类那样接下划线。
struct UrlTableProperties
{
string name;
int num_entries;
static Pool<UrlTableProperties>* pool;
};
6.6 指针变量命名
对一重指针变量的基本原则为:p
+变量类型前缀+命名。
举例:
char *pName;
对二重指针变量的基本规则为:pp
+变量类型前缀+命名。
对三重指针变量的基本规则为:ppp
+变量类型前缀+命名。
七、常量命名
7.1 总述
对于数值或者字符串等等常量的定义,建议采用全大写
字母,单词之间加 “_”
的方式命名(枚举同样建议使用此方式定义)
举例:
const float PI_ROUNDED = 3.14;
7.2 说明
常量定义时尽量加 ’const’
修饰符
八、宏命名
8.1 总述
对于数值或者字符串等等宏的定义,建议采用全大写
字母,单词之间加 “_”
的方式命名(枚举同样建议使用此方式定义)
举例:
#define MAX_FILENAME_LENGTH 20
#define PI_ROUNDED 3.0
8.2 说明
- 宏定义表达式时要用完备的括号,如:
#define RECTANGLE_AREA(a, b) a * b
以上定义存在风险,以下定义才正确:
#define RECTANGLE_AREA(a, b) ((a) *( b))
- 将宏所定义的多条表达式放在大括号中,如:
#define FOO(x) { \
printf("arg is %s\n", x); \
DoSomethingUseful(x); \
}
九、函数命名
9.1 总述
函数命名应以函数要执行的动作命名,一般采用动词或者动词+名词的结构。
举例:
void DrawBox(int point);
9.2 说明
- 内部或静态函数采用
Small-camel-case体系
,如:
void getBoxValue(void);
- 需要公开的函数(API)采用
Big-camel-case体系
,如:
void GetBoxValue(void);
- 对于首字母缩写的单词,更倾向于将它们视作一个单词进行首字母大写,如:
void StartRpc(void);
而非
void StartRPC(void);
十、命名空间命名
命名空间以小写字母命名。最高级命名空间的名字取决于项目名称。要注意避免嵌套命名空间的名字之间和常见的顶级命名空间的名字之间发生冲突。
顶级命名空间的名称应当是项目名或者是该命名空间中的代码所属的团队的名字。命名空间中的代码,应当存放于和命名空间的名字匹配的文件夹或其子文件夹中。
注意:不使用缩写作为名称
的规则同样适用于命名空间。命名空间中的代码极少需要涉及命名空间的名称,因此没有必要在命名空间中使用缩写。
要避免嵌套的命名空间与常见的顶级命名空间发生名称冲突。由于名称查找规则的存在,命名空间之间的冲突完全有可能导致编译失败。尤其是,不要创建嵌套的 std
命名空间。建议使用更独特的项目标识符 (websearch::index
, websearch::index_util
) 而非常见的极易发生冲突的名称 (比如 websearch::util
)。
对于 internal
命名空间,要当心加入到同一 internal
命名空间的代码之间发生冲突 (由于内部维护人员通常来自同一团队,因此常有可能导致冲突)。在这种情况下,请使用文件名以使得内部名称独一无二 (例如对于 frobber.h
, 使用 websearch::index::frobber_internal
)
十一、枚举命名
11.1 总述
枚举的命名应当和常量
或宏
一致: kEnumName 或是 ENUM_NAME.
11.2 说明
单独的枚举值应该优先采用常量
的命名方式. 但宏
方式的命名也可以接受. 枚举名 UrlTableErrors
(以及 AlternateUrlTableErrors
) 是类型, 所以要用大小写混合的方式。
enum UrlTableErrors
{
kOK = 0,
kErrorOutOfMemory,
kErrorMalformedInput,
};
enum AlternateUrlTableErrors
{
OK = 0,
OUT_OF_MEMORY = 1,
MALFORMED_INPUT = 2,
};
2009 年 1 月之前, 我们一直建议采用
宏
的方式命名枚举值. 由于枚举值和宏之间的命名冲突, 直接导致了很多问题. 由此, 这里改为优先选择常量
风格的命名方式. 新代码应该尽可能优先使用常量风格. 但是老代码没必要切换到常量风格, 除非宏风格确实会产生编译期问题。
• 由 Leung 写于 2019 年 4 月 18 日