一个大型项目,参与开发人员众多,每个人的编码风格迥异,为保持代码风格统一,提高代码可读性与可维护性,一个重要的约定就是命名方式。良好统一的命名方式能让我们在不需要去查找类型声明的条件下快速了解某个名字代表的含义。命名涉及目录、文件、名字空间、类型、函数、变量、枚举、宏等等。事实上,我们对代码的理解和认知是非常依赖这些命名方式。
关于命名方式,一个通用规则就是名称应具有描述性,少用缩写。尽可能使用描述性的命名, 别心疼空间,毕竟相比之下让代码易于新读者理解更重要,不要用只有项目开发者才能理解的缩写,也不要通过砍掉几个字母来缩写单词。比如:
int priceCountReader; //无缩写
int numErrors; //num是一个常见的写法
int numDnsConnections; //人人都知道 "DNS" 是什么
int n; //毫无意义
int nerr; //含糊不清的缩写
int nCompConns; //含糊不清的缩写
int wgcConnections; //只有贵团队知道wgc是什么意思
int pcReader; //pc有太多可能的解释了
int cstmrId; //删减了若干字母
注意,一些特定的广为人知的缩写是允许的,例如用 i 表示迭代变量和用 T 表示模板参数。模板参数的命名应当遵循对应的分类:类型模板参数应当遵循类型命名的规则,非类型模板应当遵循变量命名的规则。
命名规则具有一定随意性,最重要的是坚持一致性,无论你认为它们是否重要,规则总归是规则,我们应该遵守,不建议在代码中过于展示个人与众不同的风格。
1.目录与文件命名
目录与文件名建议全部小写,以下划线分隔,这种命名方式叫小蛇式(Lower Camel Case)。
可接受的目录与文件命名示例。
my_userful_class //目录
my_useful_class.h //头文件
my_userful_class.inc //插入文件
my_useful_class.cpp //源文件
目录与文件命名规则相同,C++ 源文件以 .cpp 结尾,头文件以 .h 结尾,专门插入文本的文件则以 .inc 结尾。命名时,不要使用已经存在于 /usr/include 下的文件名,即不要与系统头文件和标准库头文件同名,如 stdlib.h。
通常应尽量让文件名更加明确,比如 http_server_logs.h 就比 logs.h 要好,定义类时文件名一般成对出现,比如 foo_bar.h 和 foo_bar.cpp,对应于类 FooBar。
2.类型命名
类型命名应该使用大驼峰式命名法(Upper Camel Case),又名帕斯卡命名法(Pascal Case)。类型名称的每个单词首字母均大写, 不包含下划线。所有类型命名 —— 类、结构体、枚举、类型定义(typedef)、类型模板参数,均使用相同约定。例如:
// 类和结构体
class UrlTable { ...
class UrlTableTester { ...
struct UrlTableProperties { ...
// 类型定义
typedef hash_map<UrlTableProperties *, string> PropertiesMap;
// using 别名
using PropertiesMap = hash_map<UrlTableProperties *, string>;
// 枚举
enum UrlTableErrors { ...
// 模板参数
template<typename Type> void fooFunc(Type t);
3.名字空间命名
(1)名字空间推荐使用“小蛇式”(Lower Snake Case),即“全小写+下划线”的命名方式;
(2)顶级名字空间的名字取决于项目名称;
(3)由于名称查找规则的存在,名字空间之间的冲突完全有可能导致编译失败,所以要注意避免嵌套名字空间的名字之间和常见的顶级名字空间和标准库中名字空间的名字发生冲突,如不要创建嵌套的同名std 名字空间;
(4)不使用缩写作为名称的规则同样适用于名字空间。
示例如下:
namespace web_search {
...
}
4.函数命名
一般来说,函数(不管是全局函数还是类成员函数)名的命名方式与变量命名方式相同,采用小驼峰式命名法(Lower Camel Case), 第一个单词首字母小写,后面的单词首字母大写,没有下划线。对于首字母缩写的单词, 更倾向于将它们视作一个单词进行首字母大写。示例如下:
addTableEntry()
deleteUrl()
openFileOrDie()
StartRpc() //而非 StartRPC()。
5.变量命名
变量(包括函数参数)和数据成员(不管是静态的还是非静态)名推荐使用小驼峰式命名法。举例:
string tableName;
class TableInfo {
...
private:
string table_name; // 不好,不建议使用下划线
string tableName; // 好,小驼峰式命名法
static Pool<TableInfo>* pool; // 好
};
注意,声明为constexpr或const的变量,或在程序运行期间其值始终保持不变的常量,命名时以“const”开头,例如:
const int constDaysInAWeek = 7;
6.枚举与宏命名
枚举命名和宏命名方式一致,采用“全大写+下划线”的命名方式。下面示例中枚举名 UrlTableErrors 是类型,所以采用 Pascal 命名方法。
// 枚举命名
enum AlternateUrlTableErrors {
OK = 0,
OUT_OF_MEMORY = 1,
MALFORMED_INPUT = 2
};
// 宏命名
#define ROUND(x) ...
#define PI_ROUNDED 3.0
7.小结
以上推荐的命名方式仅供参考,并非教条,但必须要遵守的一点就是一个项目中的命名方式一定要统一,不出现散乱分化的局面,不然代码看起来将杂乱不堪。所以,在接手一个旧项目时,命名方式要与现有代码风格保持一致。
除了上文中提及的 4 种命名方式:
- 小驼峰式(lower camel case)
- 大驼峰式(upper camel case),又名帕斯卡式(Pascal case)
- 小蛇式(lower snake case)
- 大蛇式(upper snake case)
业界还有一种较为流行的变量命名方法叫匈牙利命名法,是由一位祖籍为匈牙利的杰出前微软程序员查尔斯-西蒙尼(Charles Simonyi)提出的,广泛应用于Windows环境编程中。该命名法大致规则是在每个变量名的前面加上若干表示数据属性和类型的前缀。基本原则是:变量名=属性+类型+对象描述。如d表示int,所有d开头的变量命都表示int类型。s表示char*,所有变量命以s开头的都表示C风格字符串,以g_开头的表示全局变量,以s_开头的表示静态变量,m_开头的表示类数据成员等。那么 g_dAge,则表示一个 int 类型的全局变量。
参考文献
Google C++编程风格指南
Camel case - Wikipedia
Snake case - Wikipedia
wikipedia.帕斯卡命名法
widipedia.匈牙利命名法