google开源 C / C ++项目代码规范
1.头文件
每一般一个 .cc 文件都有一个对应的 .h 文件。也有一些常见例外,如单元测试代码和只包含 main() 函数的 .cc 文件。git
正确使用头文件可令代码在可读性,文件大小和性能上大为改观。程序员
下面的规则将引导你规避使用头文件时的各类陷阱。web
1.1。自包含的头文件
TIP编程
头文件应该可以自给自足(自包含的,也就是能够做为第一个头文件被引入),以 .h 结尾。至于用来插入文本的文件,说到底它们并非头文件,因此以应 .inc 结尾不容许。出分离 -inl.h 头文件的作法。数组
全部头文件要可以自给自足。换言之,用户和重构工具不须要为特别场合而包含额外的头文件。详言之,一个头文件要有 1.2。#define保护,统计包含它所须要的其它头文件,也不要求定义任何特别的符号。缓存
不过有一个例外,即一个文件并非自足的,而是做为文本插入到代码某处。或者,文件内容其实是其它头文件的特定平台(特定于平台)扩展部分,这些文件就要用 .inc 文件扩展名。多线程
若是 .h 文件声明了一个模板或内联函数,同时也在该文件加以定义。有用凡是到这些的 .cc 文件,就得通通包含该头文件,不然程序可能会在构建中连接失败。不要把这些定义放到分离的 -inl.h 文件里(译者注:过去该规范曾提倡把定义放到-inl.h里过)。app
有个例外:若是某函数模板为全部相关模板参数显式实例化,或自己就是某类的一个私有成员,它就那么定义只能在实例化该模板的 .cc 文件里。编辑器
1.2。#define保护
TIPide
全部头文件都应该使用 #define 来防止头文件被多重包含,命名格式当是: <PROJECT>_<PATH>_<FILE>_H_ 。
为保证惟一性,头文件的命名应该基于全部项目源代码树的全路径。例如,项目 foo 中的头文件 foo/src/bar/baz.h 可按以下方式保护:
#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_
...
#endif // FOO_BAR_BAZ_H_
1.3。前置声明
TIP
尽量地避免使用前置声明。使用 #include 全部游戏须要的头文件便可。
定义:
所谓「前置声明」(forward declaration)是类,函数和模板的纯粹声明,没伴随着其定义。
优势:
前置声明可以节省编译时间,的多余 #include 会迫使compile-器展开更多的文件,处理更多的输入。
前置声明可以节省没必要要的从新编译的时间。 #include 使代码由于头文件中无关的改动而被从新编译屡次。
缺点:
前置声明隐藏了依赖关系,头文件改动时,用户的代码会跳过必要的从新编译过程。
前置声明可能会被库的后续更改所破坏。前置声明函数或模板有时会妨碍头文件开发者变更其API。例如扩大形参类型,加个自带默认参数的模板形参等等。
前置声明来自命名空间 std:: 的符号时,其行为未定义。
很难判断何时该用前置声明,时候什么用该 #include 极端状况下,用前置声明代替。 includes 甚至都会暗暗地改变代码的含义:
// bh:
struct B {};
struct D : B {}
// good_user.cc:
#包括 “BH”
void f (B * );
void f (void * );
void test (D * x ) { f (x ); } //调用f(B *)
若是 #include 被 B 状语从句: D 的前置声明替代, test() 就会调用 f(void*) 。
前置声明了include 很多来自头文件的符号时,就会比单单一行的 冗长。
仅仅为了能前置声明而重构代码(好比用指针成员代替对象成员)会使代码变得更慢更复杂。
结论:
尽可能避免前置声明那些定义在其余项目中的实体。
函数:老是使用 #include。
类模板:优先使用 #include。
至于何时包含头文件,参见 1.5。#include的路径及顺序 。
1.4。内联函数
TIP
只有当函数只有10行甚至更少时才将其定义为内联函数。
定义:
当函数被声明为内联函数以后,编译器会将其内联展开,而不是按一般的函数调用机制进行调用。
优势:
只要内联的函数体小小,内联该函数能够令目标代码更加高效。对于存取函数以及其它函数体比较短,性能关键的函数,鼓励使用内联。
缺点:
滥用内联将致使程序变得更慢。内联可能使目标代码量或增或减,这取决于内联函数的大小。内联很是短小的存取函数一般会减小代码大小,但内联一个至关大的函数将戏剧性的增长代码大小。现代处理器因为更好的利用了指令缓存,小巧的代码每每执行更快。
结论:
一个较为合理的经验准则是,不要内联超过10行的函数。谨谨对待析构函数,析构函数每每比其表面看起来要更长,由于有隐含的成员和基类析构函数被调用!
另外一个实用的经验准则:内联那些包含循环或 switch 语句的函数经常是得不偿失(除非在大多数状况下,这些循环或 switch 语句从不被执行)。
有些函数即便声明为内联的也不必定会被编译器内联,这点很重要; 好比虚函数和递归函数就不会被正常内联。一般,递归函数不该该声明成内联函数。(YuleFox注:递归调用堆栈的展开并不像循环那么简单,好比递进层数在编译时多是未知的,大多数编译器都不支持内联递归函数)。虚函数内联的主要缘由是想把它的函数体放在类定义内,为了图个方便,抑或是看成文件描述其行为,好比精短的存取函数。
1.5。 #include 的路径及顺序
TIP
使用标准的头文件包含顺序可加强可读性,避免隐藏依赖:相关头文件,C库,C ++库,其余库的 .h,本项目内的 .h。
项目内部文件应按照项目源代码目录树结构排列,避免使用UNIX特殊的快捷目录 .(当前目录)或 .. (上级目录)。例如, google-awesome-project/src/base/logging.h 应该按以下方式包含:
#include “base / logging.h”
又如, dir/foo.cc 或 dir/foo_test.cc 的主要做用英文的英文实现或测试 dir2/foo2.h 的功能, foo.cc 中包含头文件的次序以下:
dir2/foo2.h (优先位置,详情以下)
C系统文件
C ++系统文件
库其余的 .h 文件
项目本。内 .h 文件
优先这种顺序的排序保证当 dir2/foo2.h 遗漏某些必要的库时, dir/foo.cc 或 dir/foo_test.cc 的构建会马上停止。所以这一条规则保证维护这些文件的人们首先看到构建停止的消息而不是维护其余包的人们。
dir/foo.cc 和 dir2/foo2.h 一般位于同一目录下(如 base/basictypes_unittest.cc 和 base/basictypes.h),但也可放在不一样目录下。
按字母顺序分别对每种类型的头文件进行二次排序是不错的主意。注意较老的代码可不符合这条规则,要在方便的时候改正它们。
您所依赖的符号(符号)被哪些头文件所定义,您就应该包含(包括)哪些头文件,前置声明 (向前声明)状况除外。您好比要用到 bar.h 中的某个符号,哪怕您所包含的 foo.h 已经包含了 bar.h,也照样得包含 bar.h,除非foo.h 有明确 说明它会自动向您提供 bar.h 中符号。不过,凡是cc文件所对应的「相关头文件」已经包含的,就不用再重复包含进其cc文件里面了,就像 foo.cc 只包含 foo.h就够了,不用再管后者所包含的其它内容。
举#include “foo / public / fooserver.h”//优先位置
#include “foo / public / bar.h”例如, google-awesome-project/src/foo/internal/fooserver.cc 包含次序以下:
4.函数
4.1。参数顺序
总述
函数的参数顺序为:输入参数在先,后跟输出参数。
说明
C / C ++中的函数参数或者是函数的输入,或者是函数的输出,或兼而有之。输入参数一般是值参或 const 引用,输出参数或输入/输出参数则通常为非 const 指针。在排列参数顺序时,将全部的输入参数置于输出参数以前。特别要注意,在加入新参数时不要由于它们是新参数就置于参数列表最后,而是仍然要按照前述的规则,即将新的输入参数也置于输出参数以前。
这并不是一个硬性规定。输入/输出参数(一般是类或结构体)让这个问题变得复杂。而且,有时候为了其余函数保持一致,你可能不得不不全部变通。
4.2。编写简短函数
总述
咱们倾向于编写简短,凝练的函数。
说明
咱们认可长函数有时是合理的,所以并不硬限制函数的长度。若是函数超过40行,能够思索一下能不能在不影响程序结构的前提下对其进行分割。
即便一个长函数如今工做的很是好,一旦有人对其修改,有可能出现新的问题,甚至致使难以发现的错误。使函数尽可能简短,以便于他在他人阅读和修改代码。
在处理代码时,你可能会发现复杂的长函数。不要惧怕修改现有代码:若是证明这些代码使用/调试起来很困难,或者你只需要使用其中的一小段代码,考虑将其分割为更加简短并易于管理的若干函数。
4.3。引用参数
总述
全部按引用传递的参数必须加上 const。
定义
在C语言中,若是函数须要修改变量的值,参数必须为指针,如 。在C ++中,函数还能够声明为引用参数: 。int foo(int *pval)int foo(int &val)
优势
引用定义参数能够防止出现 (*pval)++ 这样丑陋的代码。引用参数对于拷贝构造函数这样的应用也是必需的。同时也更明确地不接受空指针。
缺点
容易引发误解,由于引用在语法上是值变量却拥有指针的语义。
结论
函数参数列表中,全部引用参数都必须是 const:
void Foo (const string &in , string * out );
事实上这在Google Code是一个硬性约定:输入参数是值参或 const 引用,输出参数为指针。输入参数能够是 const 指针,但决不能是非 const 引用参数,除非特殊要求,好比 swap()。
有时候,在输入形参中用针指 比 更明智。好比:const T*const T&
可能会传递空指针。
函数要把指针或对地址的引用赋值给输入形参。
总而言之,大多时候输入形参每每是 。用若 则说明输入侧另有处理。因此若要使用 ,则应给出相应的理由,不然会使读者感到迷惑。const T&const T*const T*
4.4。函数重载
总述
若要使用函数重载,则必须能让读者一看调用点就成竹在胸,而不用花心思猜想调用的重载函数究竟是哪种。这一规则也适用于构造函数。
定义
你能够编写一个参数类型为 的函数,而后用另外一个参数类型为 的函数对其进行重载:const string&const char*
class MyClass {
public :
void Analyze (const string &text );
void 分析(const char * text , size_t textlen );
};
优势
经过重载参数不一样的同名函数,能够令代码更直观。模板化代码须要重载,这同时也能为使用者带来便利。
缺点
若是函数单靠不一样的参数类型而重载(acgtyrant注:这意味着参数数量不变),读者就得十分熟悉C ++五花八门的匹配规则,以了解匹配过程具体到底如何。另外,若是派生类只重载了某个函数的部分变体,继承语义就容易使人困惑。
结论
若是打算重载一个函数,能够试试改在函数名里加参数信息。例如,用 AppendString()和 AppendInt() 等,而不是一口气重载多个 Append()。若是重载函数的目的是为了支持不一样数量的同一类型参数,则优先考虑使用 std::vector 以便使用者能够用 列表初始化指定参数。
4.5。缺省参数
总述
只容许在非虚函数中使用缺省参数,且必须保证缺省参数的值始终一致。参数缺省与 函数重载 遵循一样的规则。通常状况下建议使用函数重载,尤为是在缺省函数带来的可读性提高不能弥补下文中所提到的缺点的状况下。
优势
有些函数通常状况下使用默认参数,但有时须要又使用非默认的参数。缺省参数为这样的情形提供了便利,使程序员不须要为了极少的例外状况编写大量的函数。和函数重载相比,缺省参数的语法更简洁明了,减小了大量的样板代码,也更好地区别了“必要参数”和“可选参数”。
缺点
缺省参数其实是函数重载语义的另外一种实现方式,所以全部 不该当使用函数重载的理由 也都适用于缺省参数。
虚函数调用的缺省参数取决于目标对象的静态类型,此时没法保证给定函数的全部重载声明的都是一样的缺省参数。
缺省参数是在每一个调用点都要进行从新求值的,这会形成生成的代码迅速膨胀。做为读者,通常来讲也更但愿缺省的参数在声明时就已经被固定了,而不是在每次调用时均可能会有不一样的取值。
缺省参数会干扰函数指针,致使函数签名与调用点的签名不一致。而函数重载不会致使这样的问题。
结论
对于虚函数,不容许使用缺省参数,由于在虚函数中缺省参数不必定能正常工做。若是在每一个调用点缺省参数的值都有可能不一样,在这种状况下缺省函数也不容许使用。(例如,不要写像 这样的代码。)void f(int n = counter++);
在其余状况下,若是缺省参数对可读性的提高远远超过了以上说起的缺点的话,能够使用缺省参数。若是仍有疑惑,就使用函数重载。
4.6。函数返回类型后置语法
总述
只有在常规写法(返回类型前置)不便于书写或不便于阅读时使用返回类型后置语法。
定义
C ++如今容许两种不一样的函数声明方式。以往的写法是将返回类型置于函数名以前。例如:
int foo (int x );
C ++ 11引入了这一新的形式。如今能够在函数名前使用 auto 关键字,在参数列表以后后置返回类型。例如:
auto foo (int x ) - > int ;
后置返回类型为函数做用域。对于像 int 这样简单的类型,两种写法没有区别。但对于复杂的状况,例如类域中的类型声明或者以函数参数的形式书写的类型,写法的不一样会形成区别。
优势
后置返回类型是显式地指定 Lambda表达式 的返回值的惟一方式。某些状况下,编译器能够自动推导出Lambda表达式的返回类型,但并非在全部的状况下都能实现。即便编译器可以自动推导,显式地指定返回类型也能让读者更明了。
有时在已经出现了的函数参数列表以后指定返回类型,可以让书写更简单,也更易读,尤为是在返回类型依赖于模板参数时。例如:
template < class T , class U > auto add (T t , U u ) - > decltype (t + u );
对比下面的例子:
template < class T , class U > decltype (declval < T &> () + declval < U &gt ;) add (T t , U u );
缺点
后置返回类型相对来讲是很是新的语法,并且在C和Java中都没有类似的写法,所以可能对读者来讲比较陌生。
在已有的代码中有大量的函数声明,你不可能把它们都用新的语法重写一遍。所以实际的作法只能是使用旧的语法或者新旧混用。在这种状况下,只使用一种版本是相对来讲更规整的形式。
结论
在大部分状况下,应当继续使用以往的函数声明写法,即将返回类型置于函数名前。只有在必要的时候(如Lambda表达式)或者使用后置语法可以简化书写而且提升易读性的时候才使用新的返回类型后置语法。可是后一种状况通常来讲是不多见的,大部分时候都出如今至关复杂的模板代码中,而多数状况下不鼓励写这样 复杂的模板代码。
7.命名约定
最重要的一致性规则是命名管理。命名的风格能让咱们在不须要去查找类型声明的条件下快速地了解某个名字表明的含义:类型,变量,函数,常量,宏,等等,甚至。咱们大脑中的模式匹配引擎很是依赖这些命名规则。
命名规则具备必定随意性,但相比按我的喜爱命名,一致性更重要,因此不管你认为它们是否重要,规则总归是规则。
7.1。通用命名规则
总述
函数命名,变量命名,文件命名要有描述性; 少用缩写。
说明
尽量使用描述性的命名,别心疼空间,毕竟相比之下让代码易于新读者理解更重要。不要用只有项目开发者能理解的缩写,也不要经过砍掉几个字母来缩写单词。
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 表示模板参数。
模板参数的命名应当遵循对应的分类:类型模板参数应当遵循 类型命名 的规则,而非类型模板应当 遵循变量命名 的规则。
7.2。文件命名
总述
文件名要所有小写,能够包含下划线(_)或连-字符(),依照项目的约定。若是没有约定,那么“ _” 更好。
说明
可接受的文件命名示例:
my_useful_class.cc
my-useful-class.cc
myusefulclass.cc
myusefulclass_test.cc // _unittest 状语从句: _regtest 已弃用。
C ++文件要以 .cc 结尾,头文件以 .h 结尾。专门插入文本的文件则以 .inc 结尾,参见 头文件自足。
不要使用已经存在于 /usr/include 下的文件名(Yang.Y注:即编译器搜索系统头文件的路径),如 db.h。
一般应尽可能让文件名更加明确。 http_server_logs.h 就比 logs.h 要好。定义类时文件名通常成对出现,如 foo_bar.h 和 foo_bar.cc,对应于类 FooBar。
联内必须函数放在 .h 文件中。若是内联函数比较短,就直接放在 .h 中。
7.3。类型命名
总述
类型名称的每一个单词首字母均大写,不包含下划线: MyExcitingClass, MyExcitingEnum。
说明
全部类型命名 - 类,结构体,类型定义(typedef),枚举,类型模板参数 - 均使用相同约定,即以大写字母开始,每一个单词首字母均大写,不包含下划线。例如:
//类和结构体
类 UrlTable { ...
class UrlTableTester { ...
struct UrlTableProperties { ...
//类型定义
typedef hash_map < UrlTableProperties * , string > PropertiesMap ;
//使用别名
使用 PropertiesMap = hash_map < UrlTableProperties * , string > ;
//枚举
enum UrlTableErrors { ...
7.4。变量命名
总述
变量(包括函数参数)和数据成员名一概小写,单词之间用下划线链接。类的成员变量如下划线结尾,但结构体的就不用,如: a_local_variable, a_struct_data_member, a_class_data_member_。
说明
普通变量命名
举例:
字符串 table_name ; //好 - 用下划线。
字符串 表名; //好 - 全小写。
字符串 tableName ; //差 - 混合大小写
类数据成员
无论是静态的仍是非静态的,类数据成员均可以和普通变量同样,但要接下划线。
类 TableInfo {
...
private :
string table_name_ ; //好 - 后加下划线。
字符串 tablename_ ; //好。
静态 池< TableInfo > * pool_ ; //好。
};
结构体变量
无论是静态的仍是非静态的,结构体数据成员均可以和普通变量同样,不用像类那样接下划线:
struct UrlTableProperties {
string name ;
int num_entries ;
静态 池< UrlTableProperties > * 池;
};
结构体与类的使用讨论,参考 结构体与类。
7.5。常量命名
总述
声明为 constexpr 或 const 的变量,或在程序运行期间其值始始保持不变的,命名时以“k”开头,大小写混合。例如:
const int kDaysInAWeek = 7 ;
说明
全部具备静态存储类型的变量(例如静态变量或全局变量,参见 存储类型)都应当以此方式命名。对于其余存储类型的变量,如自动变量等,这条规则是可选的。若是不采用这条规则,就按照通常的变量命名规则。
7.7。函数命名
总述
常规函数使用大小写混合,取值和设值函数则要求与变量名匹配: MyExcitingFunction(), MyExcitingMethod(), my_exciting_member_variable(), set_my_exciting_member_variable()。
说明
通常来讲,函数名的每一个单词首字母大写(即“驼峰变量名”或“帕斯卡变量名”),没有下划线。对于首字母缩写的单词,更倾向于将它们视做一个单词进行首字母大写(例如,写做 StartRpc() 而非 StartRPC())。
AddTableEntry ()
DeleteUrl ()
OpenFileOrDie ()
(一样的命名规则同时适用于类做用域和命名空间做用域的常量,由于它们是做为API的一部分暴露对外的,所以应当让它们看起来像是一个函数,由于在这时,它们其实是一个对象而非函数的这一事实对外不过是一个可有可无的实现细节。)
取值和设值函数的命名与变量一致。通常来讲它们的名称与实际的成员变量对应,但并不强制要求。例如 与 。int count()void set_count(int count)
7.7。命名空间命名
总述
命名空间以小写字母命名。最高级命名空间的名字取决于项目名称。要注意避免嵌套命名空间的名字之间和常见的顶级命名空间的名字之间发生冲突。
顶级命名空间的名称应当是项目名或者是该命名空间中的代码所属的团队的名字。命名空间中的代码,应当存放于和命名空间的名字匹配的文件夹或其子文件夹中。
注意 不使用缩写做为名称 的规则一样适用于命名空间。命名空间中的代码极少须要涉及命名空间的名称,所以没有必要在命名空间中使用缩写。
要避免嵌套的命名空间与常见的顶级命名空间发生名称冲突。因为名称查找规则的存在,命名空间之间的冲突彻底有可能致使编译失败。尤为是,不要建立嵌套的 std 命名空间。建议使用更独特的项目标识符(websearch::index, websearch::index_util)而很是见的极易发生冲突的名称(好比 websearch::util)。
对于 internal 命名空间,要小心加入到同一 internal 命名空间的代码之间发生冲突(因为内部维护人员一般来自同一团队,所以常有可能致使冲突)。在这种状况下,请使用文件名以使内部名称独一无二(例如对于 frobber.h,使用 websearch::index::frobber_internal)。
7.8。枚举命名
总述
的枚举命名应当状语从句: 常量 或 宏 harmony和谐: kEnumName 或是 ENUM_NAME。
说明
的单独枚举值应该优先采用 常量 的命名方式。但 宏 方式的命名也。能够接受。枚举名 UrlTableErrors (以及 AlternateUrlTableErrors)是类型,因此要用大小写混合的方式。
enum UrlTableErrors {
kOK = 0 ,
kErrorOutOfMemory ,
kErrorMalformedInput ,
};
枚举 AlternateUrlTableErrors {
OK = 0 ,
OUT_OF_MEMORY = 1 ,
MALFORMED_INPUT = 2 ,
};
2009年1月以前,咱们一直建议采用 宏 的方式命名枚举值。因为枚举值和宏之间的命名冲突,直接致使了不少问题。由此,这里改成优先选择常量风格的命名方式。新代码应该尽量优先使用常量风格。可是老代码不必切换到常量风格,除非宏风格确实会产生编译期问题。
7.9。宏命名
总述
你并不打算 使用宏,对吧?若是你必定要用,像这样命名: MY_MACRO_THAT_SCARES_SMALL_CHILDREN。
说明
参考 预处理宏 ; 一般 不该该 使用宏。若是不得不使用,其命名像枚举命名同样所有大写,使用下划线:
#define ROUND(x)...
#define PI_ROUNDED 3.0
7.10。命名规则的特例
总述
若是你命名的实体与已有C / C ++实体类似,可参考现有命名策略。
bigopen():函数名,参照 open() 的形式
uint: typedef
bigpos: struct 或 class,参照 pos 的形式
sparse_hash_map:STL型实体; 参照STL命名约定
LONGLONG_MAX:常量,如同 INT_MAX
8.注意
注释虽然写起来很痛苦,但对保证代码可读性相当重要。下面的规则描述了如何注释以及在哪儿注释。固然也要记住:注释当然很重要,但最好的代码应当自己就是文档。有意义的类型名和变量名,要远赛过要用注释解释的含糊不清的名字。
你写的注释是给代码读者看的,也就是下一个须要理解你的代码的人。因此慷慨些吧,下一个读者可能就是你!
8.1。注释风格
总述
使用 // 或 ,统一就好。/* */
说明
// 或 均可以; 但 更 经常使用。要在如何注释及注释风格上确保统一。/* *///
8.2。文件注释
总述
在每个文件开头加入版权公告。
文件注释描述了该文件的内容。若是一个文件只声明,或实现或测试了一个对象,而且这个对象已经在它的声明处进行了详细的注释,那么就没有必要再加上文件注释。除此以外的其余文件都须要文件注释。
说明
法律公告和做者信息
每一个文件都应该包含许可证引用。为项目选择合适的许可证版本(好比,Apache 2.0,BSD,LGPL,GPL)
若是你对原始做者的文件作了重大修改,请考虑删除原做者信息。
文件内容
若是一个 .h 文件声明了多个概念,则文件注释应当对文件的内容作一个大体的说明,同时说明各个概念之间的联系。一个一到两行的文件注释就足够了,对于每一个概念的详细文档应当放在各个概念中,而不是文件注释中。
不要在 .h 和 .cc 之间复制注释,这样的注释偏离了注释的实际意义。
8.3。类注释
总述
每一个类的定义都要附带一份注释,描述类的功能和用法,除非它的功能至关明显。
//遍历GargantuanTable的内容。
//示例:
// GargantuanTableIterator * iter = table-> NewIterator();
// it for(iter-> Seek(“foo”);!iter-> done(); iter-> Next()){
// process(iter-> key(),iter-> value());
//}
//删除它;
类 GargantuanTableIterator {
...
};
说明
类注释应当为读者理解如何使用与什么时候使用类提供足够的信息,同时应当提醒读者在正确使用此类时应当考虑的因素。若是类有任何同步前提,请用文档说明。若是该类的实例可被多线程访问,要特别注意文档说明多线程环境下相关的规则和常量使用。
若是你想用一小段代码演示这个类的基本用法或一般用法,放在类注释里也很是合适。
若是类的声明和定义分开了(例如分别放在了 .h 和 .cc 文件中),此时,描述类用法的注释应当和接口定义放在一块儿,描述类的操做和实现的注释应当和实现放在一块儿。
8.4。函数注释
总述
函数声明处的注释描述函数功能; 定义处的注释描述函数实现。
说明
函数声明
基本上每一个函数声明处前都应当加上注释,描述函数的功能和用途。只有在函数的功能简单而明显时才能省略这些注释(例如,简单的取值和设值函数)。注释使用叙述式(“打开文件”)而非指令式(“打开文件”); 注释只是为了描述函数,而不是命令函数作什么。一般,注释不会描述函数如何工做。那是函数定义部分的事情。
函数声明处注释的内容:
函数的输入输出。
对类成员函数而言:函数调用期间对象是否须要保持引用参数,是否会释放这些参数。
函数是否分配了必须由调用者释放的空间。
参数是否能够为空指针。
是否存在函数使用上的性能隐患。
若是函数是可重入的,其同步提提是什么?
举例以下:
//返回此表的迭代器。
当迭代器完成时,它是
客户端的责任//而且一旦
建立
迭代器的GargantuanTable对象被删除,它就不能使用迭代器。//
//迭代器最初位于表的开始位置。
//
//此方法等同于:
// Iterator * iter = table-> NewIterator();
// iter-> Seek(“”);
//返回iter;
//若是您要当即寻找到
返回的迭代器
中的其余位置,则使用NewIterator()会更快,并避免额外的查找。
Iterator * GetIterator () const;
但也要避免罗罗嗦嗦,或者对显着易见的内容进行说明。下面的注释就没有必要加上“不然返回false”,由于已经暗含其中了:
//若是表不能包含更多条目,则返回true。
bool IsTableFull ();
注释函数重载时,注释的重点应该是函数中被重载的部分,而不是简单的重复被重载的函数的注释。多数状况下,函数重载不须要额外的文档,所以也没有必要加上注释。
注释构造/析构函数,切记读代码的人知道构造/析构函数的全部功能,因此“销毁这一对象”这样的注释是没有意义的。你应该注意的是注意构造函数对参数作了什么(例如,是否取得指针全部权)以及析构函数清理了什么。若是都是些可有可无的内容,直接省掉注释。析构函数前没有注释是很正常的。
函数定义
若是函数的实现过程当中用到了很巧妙的方式,那么在函数定义处应当加上解释性的注释。例如,你所使用的编程技巧,实现的大体步骤,或解释如此实现的理由。举个例子,你能够说明为何函数的前半部分要加锁然后半部分不须要。
不要 从 .h 文件或其余地方的函数声明处直接复制注释。简要重述函数功能是能够的,但注释重点要放在如何实现上。
8.5。变量注释
总述
一般变量名自己足以很好说明变量用途。某些状况下,也须要额外的注释说明。
说明
类数据成员
每一个类数据成员(也叫实例变量或成员变量)都应该用注释说明用途。若是有非变量的参数(例如特殊值,数据成员之间的关系,生命周期等)不可以使用类型与变量名明确表达,则应当加上注释。然而,若是变量类型与变量名已经足够描述一个变量,那么就不须要加上注释。
特别地,若是变量能够接受 NULL 或 -1 等警惕值,须加以说明。好比:
private :
//用于限制检查表访问。-1意味着
//咱们还不知道表中有多少个条目。
int num_total_entries_ ;
全局变量
和数据成员同样,全部全局变量也要注释说明含义及用途,以及做为全局变量的缘由。好比:
//在此回归测试中咱们经历的测试用例的总数。
const int kNumTestCases = 6 ;
8.6。实现注释
总述
对于代码中巧妙的,晦涩的,有趣的,重要的地方加以注释。
说明
代码前注释
巧妙或复杂的代码段前要加注释。好比:
//将结果除以2,考虑到x
//包含来自add的进位。
for (int i = 0 ; i < result - > size (); i ++ ) {
x = (x << 8 ) + (* result )[ i ];
(* 结果)[ i ] = x >> 1 ;
x &= 1 ;
}
行注释
比较隐晦的地方要在行尾加入注释。在行尾空两格进行注释。好比:
//若是咱们有足够的内存,也能够对数据部分进行mmap。
mmap_budget = max < int64 > (0 , mmap_budget - index_ - > length ());
if (mmap_budget > = data_size_ && !MmapData (mmap_chunk_bytes , mlock ))
return ; //错误已经记录。
注意,这里用了两段注释分别描述这段代码的做用,并提示函数返回错误已经被记入日志。
若是你须要连续进行多行注释,能够使之对齐得到更好的可读性:
DoSomething (); //在这里发表评论,以便评论排成一行。
DoSomethingElseThatIsLonger (); //代码和注释之间有两个空格。
{ //容许打开一个新的做用域时,在注释以前的一个空格
// //所以注释与下面的注释和代码一块儿排列。
DoSomethingElse (); //一般在行注释以前有两个空格。
}
std :: vector < string > list {
//支撑列表中的注释描述下一个元素...
“First item” ,
// ..而且应该适当地对齐。
“第二项” };
作一点事(); / *对于尾部块注释,一个空间能够。* /
函数参数注释
若是函数参数的意义不明显,考虑用下面的方式进行弥补:
若是参数是一个字面常量,而且这一常量在多处函数调用中被使用,用以推断它们一致,你应该用一个常量名让这个约定变得更明显,而且保证这一约定不会被打破。
考虑更改函数的签名,让某个 bool 类型的参数变为 enum 类型,这样可让这个参数的值表达其意义。
若是某个函数有多个配置选项,你能够考虑定义一个类或结构体以保存全部的选项,并传入类或结构体的实例。这样的方法有许多优势,例如这样的选项能够在调用处用变量名引用,这样就能清晰地代表其意义。同时也减小了函数参数的数量,使得函数调用更易读也易写。除此以外,以这样的方式,若是你使用其余的选项,就无需对调用点进行更改。
用具名变量代替大段而复杂的嵌套表达式。
万不得已时,才考虑在调用点用注释阐明参数的意义。
好比下面的示例的对比:
//这些论据是什么?
const DecimalNumber product = CalculateProduct (values , 7 , false , nullptr );
和
ProductOptions 选项;
选项。set_precision_decimals (7 );
选项。set_use_cache (ProductOptions :: kDontUseCache );
const DecimalNumber product =
CalculateProduct (values , options , / * completion_callback = * / nullptr );
哪一个更清晰一目了然。
不容许的行为
不要描述显而易见的现象, 永远不要 用天然语言翻译代码做为注释,除非即便对深刻理解C ++的读者来讲代码的行为都是不明显的。要假设读代码的人C ++水平比你高,即使他/她可能不知道你的用意:
你所提供的注释应当解释代码 为何 要这么作和代码的目的,或者最好是让代码自文档化。
比较这样的注释:
//在矢量中查找元素。< - 差:这太明显了!
自动 ITER = STD :: 找到(v 。开始(), v 。端(), 元素);
若是 (ITER =! v 。端()) {
过程(元件);
}
和这样的注释:
//处理“元素”,除非它已经被处理。
自动 ITER = STD :: 找到(v 。开始(), v 。端(), 元素);
若是 (ITER =! v 。端()) {
过程(元件);
}
自文档化的代码根本就不须要注释。上面例子中的注释对下面的代码来讲就是毫无必要的:
if (!IsAlreadyProcessed (element )) {
Process (element );
}
8.8。标点,拼写和语法
总述
注意标点,拼写和语法; 写的好的注释比差的要易读的多。
说明
注释的一般写法是包含正确大小写和结尾句号的完整叙述性语句。大多数状况下,完整的句子比句子片断可读性更高。短一点的注释,好比代码行尾注释,能够随意点,但依然要注意风格的一致性。
虽然被别人指出该用分号时却用了逗号多少有些尴尬,但清晰易读的代码仍是很重要的。正确的标点,拼写和语法对此会有很大帮助。
8.8。TODO注释
总述
对那些临时的,短时间的解决方案,或已经够好,但仍不完美的代码使用 TODO 注释。
TODO 注意要使用全大写的字符串 TODO,在随后的圆括号里写上你的名字,邮件地址,bug ID,或其它身份标识和与这一 TODO 相关的问题。主要目的是让添加注释的人(也是能够请求提供更多细节的人)可根据规范的 TODO 格式进行查找。添加 TODO 注释并不意味着你要本身来修正,所以当你加上带有姓名的时候 TODO ,通常都是写上本身的名字。
// TODO(kl@gmail.com):这里使用“*”做为链接运算符。
// TODO(Zeke)将其改成使用关系。
// TODO(错误12345):删除“最后访问者”功能
若是加 TODO 是为了在“未来某一天作某事”,能够附上一个很是明确的时间“Fix by November 2005”),或者一个明确的事项(“全部客户端均可以处理XML响应时删除此代码。”) 。
8.9。弃用注释
总述
经过弃用注释(DEPRECATED 评论)以标记某接口点已弃用。
您能够写上包含全大写的 DEPRECATED 注释,以标记某接口为弃用状态。注释能够放在接口声明前,或者同一行。
在DEPRECATED 一词后,在 括号中留下您的名字,邮箱地址以及其余身份标识。
弃用注释应当包涵简短而清晰的指引,以帮助其余人修复其调用点。在C ++中,你能够将一个弃用函数改形成一个内联函数,这一函数将调用新的接口。
DEPRECATED 仅仅标记接口为并 不容许你们不约而同地弃用,您还得亲自主动修正调用点(callsites),或是找个帮手。
修正好的代码应该不会再涉及弃用接口点了,着实改用新接口点。若是您不知从何下手,能够找标记弃用注释的当事人一块儿商量。
9.格式
每一个人均可能有本身的代码风格和格式,但若是一个项目中的全部人都遵循同一风格的话,这个项目就能更顺利地进行。每一个人未必能赞成下述的每一处格式规则,并且其中的很多规则须要必定时间的适应,但整个项目服从统一的编程风格是很重要的,只有这样才能让全部人轻松地阅读和理解代码。
为了帮助你正确的格式化代码,咱们写了一个 emacs配置文件。
9.1。行长度
总述
每一行代码字符数不超过80。
咱们也认识到这条规则是有争议的,但不少已有代码都遵守这一规则,所以咱们感受一致性更重要。
优势
提倡该原则的人认为强迫他们调整编辑器窗口大小是很野蛮的行为。不少人同时并排开几个代码窗口,根本没有多余的空间拉伸窗口。你们都把窗口最大尺寸加以限定,而且80列宽是传统标准。那么为何要改变呢?
缺点
反对该原则的人则认为更宽的代码行更易阅读。80列的限制是上个世纪60年代的大型机的古板缺陷; 现代设备具备更宽的显示屏,能够很轻松地显示更多代码。
结论
80个字符是最大值。
若是没法在不伤害易读性的条件下进行断行,那么注释行能够超过80个字符,这样能够方便复制粘贴。例如,带有命令示例或URL的行能够超过80个字符。
长全部游戏的路径 #include 语句能够超出80列。
文件头保护 能够无视该原则。
9.2。非ASCII字符
总述
尽可能不使用非ASCII字符,使用时必须使用UTF-8编码。
说明
即便是英文,也不该将用户界面的文本硬编码到源代码中,所以非ASCII字符应当不多被用到。特殊状况下能够适当包含此类字符。例如,代码分析外部数据文件时,能够适当硬编码数据文件中做为分隔符的非ASCII字符串; 更常见的是(不须要本地化的)单元测试代码可能包含非ASCII字符串。此类状况下,应使用UTF-8编码,由于不少工具均可以理解和处理UTF-8编码。
十六进制编码也能够,能加强可读性的状况下尤为鼓鼓 - 好比 "\xEF\xBB\xBF",或者更简洁地写做 u8"\uFEFF",在Unicode中是 零宽度无间断 的间隔符号,若是不用十六进制直接放在UTF -8格式的源文件中,是看不到的。
(Yang.Y注: "\xEF\xBB\xBF" 一般用做带编码标记的UTF-8)
使用 u8 前缀把带 uXXXX 转义序列的字符串字面值编码成UTF-8。不要用在自己就带UTF-8字符的字符串字面值上,由于若是编译器不把源代码识别成UTF-8,输出就会出错。
别用C ++ 11的 char16_t 和 char32_t,它们和UTF-8文本没有关系, wchar_t 同理,除非你写的代码要调用Windows API,后者普遍使用了 wchar_t。
9.3。空格仍是制表位
总述
只使用空格,每次缩进2个空格。
说明
咱们使用空格缩进。不要在代码中使用制表符。你应该设置编辑器将制表符转为空格。
9.4。函数声明与定义
总述
返回类型和函数名在同一行,参数也尽可能放在同一行,若是放不下就对形参分行,分行方式与 函数调用 一致。
说明
函数看上去像这样:
ReturnType ClassName :: FunctionName (Type par_name1 , Type par_name2 ) {
DoSomething ();
...
}
若是同一行文本太多,放不下全部参数:
ReturnType ClassName :: ReallyLongFunctionName (类型 par_name1 , 类型 par_name2 ,
类型 par_name3 ) {
DoSomething ();
...
}
甚至连第一个参数都放不下:
ReturnType LongClassName :: ReallyReallyReallyLongFunctionName (
Type par_name1 , // 4 space indent
Type par_name2 ,
Type par_name3 ) {
DoSomething (); // 2空格缩进
...
}
注意如下几点:
使用好的参数名。
只有参数未被使用或者其用途很是明显时,才能省略参数名。
若是返回类型和函数名在一行放不下,分行。
若是返回类型与函数声明或定义分行了,不要缩进。
左圆括号老是和函数名在同一行。
函数名和左圆括号间永远没有空格。
圆括号与参数间没有空格。
左大括号总在最后一个参数同一行的末尾处,不另起新行。
右大括号老是单独位于函数最后一行,或者与左大括号同一行。
右圆括号和左大括号间老是有一个空格。
全部形参应尽量对齐。
缺省缩进为2个空格。
换行后的参数保持4个空格的缩进。
未被使用的参数,或者根据上下文很容易看出其用途的参数,能够省略参数名:
类 美孚 {
公共:
美孚(富&& );
Foo (const Foo &);
Foo & operator = (Foo && );
Foo & operator = (const Foo &);
};
未被使用的参数若是其用途不明显的话,在函数定义处将参数名注释起来:
class Shape {
public :
virtual void Rotate (double radians ) = 0 ;
};
class Circle : public Shape {
public:
void Rotate(double radians) override;
};
void Circle::Rotate(double /*radians*/) {}
// 差 - 若是未来有人要实现, 很难猜出变量的做用.
void Circle::Rotate(double) {}
属性, 和展开为属性的宏, 写在函数声明或定义的最前面, 即返回类型以前:
MUST_USE_RESULT bool IsOK();
9.5. Lambda 表达式
总述
Lambda 表达式对形参和函数体的格式化和其余函数一致; 捕获列表同理, 表项用逗号隔开.
说明
若用引用捕获, 在变量名和 & 之间不留空格.
int x = 0;
auto add_to_x = [&x](int n) { x += n; };
短 lambda 就写得和内联函数同样.
std::set<int> blacklist = {7, 8, 9};
std::vector<int> digits = {3, 9, 1, 8, 4, 7, 1};
digits.erase(std::remove_if(digits.begin(), digits.end(), [&黑名单](诠释 我) {
返回 黑名单。找到(i ) != 黑名单。end ();
}),
数字。end ());
9.6。函数调用
总述
要么一行写完函数调用,要么在圆括号里对参数分行,要么参数另起一行且缩进四格。若是没有其它顾虑的话,尽量精简行数,好比把多个参数适当地放在同一行里。
说明
函数调用遵循以下形式:
bool retval = DoSomething (argument1 , argument2 , argument3 );
若是同一行放不下,可断为多行,后面每一行都和第一个实参对齐,左圆括号后和右圆括号前不要留空格:
bool retval = DoSomething (averyveryveryverylongargument1 ,
argument2 , argument3 );
参数也能够放在次行,缩进四格:
若是 (...) {
...
...
if (...) {
DoSomething (
argument1 , argument2 , // 4空格缩进
argument3 , argument4 );
}
把多个参数放在同一行以减小函数调用所需的行数,除非影响到可读性。有人认为把每一个参数都独立成行,不只更好读,并且方便编辑参数。不过,比起所谓的参数编辑,咱们更看重可读性,且后者比较好办:
若是一些参数自己就是略复杂的表达式,且下降了可读性,那么能够直接建立临时变量描述该表达式,并传递给函数:
int my_heuristic = scores[x] * y + bases[x];
bool retval = DoSomething(my_heuristic, x, y, z);
或者放着无论, 补充上注释:
bool retval = DoSomething(scores[x] * y + bases[x], // Score heuristic.
x, y, z);
若是某参数独立成行, 对可读性更有帮助的话, 那也能够如此作. 参数的格式处理应当以可读性而非其余做为最重要的原则.
此外, 若是一系列参数自己就有必定的结构, 能够酌情地按其结构来决定参数格式:
//经过3x3矩阵转换小部件。
my_widget 。变换(x1 , x2 , x3 ,
y1 , y2 , y3 ,
z1 , z2 , z3 );
9.7。列表初始化格式
总述
您平时怎么格式化函数调用,就怎么格式化 列表初始化。
说明
若是列表初始化伴随着名字,好比类型或变量名,格式化时将将名称视图函数调用名, {}视图函数调用的括号。若是没有名字,就视做名字长度为零。
//一行列表初始化示范。
返回 { foo , bar };
functioncall ({ foo , bar });
pair < int , int > p { foo , bar };
//当不得不行时。
SomeFunction (
{ “在{” } 以前假定一个零长度的名字,//假设在{前有长度为零的名字
。some_other_function_parameter );
SomeType 变量{
some , other , values ,
{ “假设在{” } 以前有一个零长度的名字,//假设在{前有长度为零的名字。
SomeOtherType {
“很是长的字符串须要周围的中断。” , //很是长的字符串,先后都须要断行。
一些, 其它 的值},
SomeOtherType { “略短字符串” , //稍短的字符串。
一些, 其它, 值}};
SomeType 变量{
“这太长了,没法将全部内容放在一行中” }; //字符串过长,所以没法放在同一行。
MyType m = { //注意了,您能够在{前断行。
superlongvariablename1 ,
superlongvariablename2 ,
{ short , interior , list },
{ interiorwrappinglist ,
interiorwrappinglist2 }};
9.9。条件语句
总述
倾向于不在圆括号内使用空格。关键字 if 状语从句: else 另起一行。
说明
对基本条件语句有两种能够接受的格式。一种在圆括号和条件之间有空格,另外一种没有。
最多见的是没有空格的格式。哪种均可以,最重要的是 保持一致。若是你是在修改一个文件,参考当前已有格式。若是是写新的代码,请参考目录下或项目中的其它文件。还在犹豫的话,就不要加空格了。
if (condition ) { //圆括号里没有空格。
... // 2空格缩进。
} else if (...) { // else与if的右括号同一行。
...
} 其余 {
...
}
若是你更喜欢在圆括号内部加空格:
if ( condition ) { //圆括号与空格紧邻 - 不常见
... // 2空格缩进。
} else { // else与if的右括号同一行。
...
}
全部注意下状况 if 状语从句:左圆括号间都有个空格。右圆括号和左大括号之间也要有个空格:
if (condition ) //差 - IF后面没空格。
if (condition ){ //差 - {前面没空格。
if (condition ){ //变本加厉地差。
if (condition ) { //好 - IF和{都与空格紧邻。
若是能加强可读性,简短的条件语句容许写在同一行。当只有简单语句而且没有使用 else [主语]时使用:
if (x == kFoo ) 返回 新的 Foo ();
if (x == kBar ) 返回 新的 Bar ();
若是有语句 else 分支则不容许:
//不容许 - 当有ELSE分支时如块块写在同一行
if (x ) DoThis ();
其余 DoThat ();
一般,单行语句不须要使用大括号,若是你喜欢用也没问题; 复杂的条件或循环语句用大括号可读性会更好。有也。项目一些要求 if 必须老是使用大括号:
若是 (条件)
DoSomething (); // 2空格缩进。
if (condition ) {
DoSomething (); // 2空格缩进。
}
但若是语句中 if-else 某个分支使用了大括号的话,其它分支也必须使用:
//不能够这样子 - 若是有大括号ELSE却没有。
if (condition ) {
foo ;
} else
bar ;
//不能够这样子 - ELSE有大括号IF却没有。
若是 (条件)
foo ;
else {
bar ;
}
//只要其中一个分支用了大括号,两个分支都要用上大括号。
if (condition ) {
foo ;
} else {
bar ;
}
9.9。循环和开关选择语句
总述
switch 语句能够使用大括号分段,以代表cases之间不是连在一块儿的。在单语句循环里,括号可用可不用。循环空应行业释义体育使用 {} 或 continue。
说明
switch 语句中的 case 块能够使用大括号也能够不用,取决于你的我的喜爱。若是用的话,要按照下文所述的方法。
若是有不知足 case 条件的枚举值, switch 应该default 老是包含一个 匹配(若是有输入值没有case去处理,编译器将给出警告)。若是 default 应该永远执行不到,简单的加条 assert:
switch (var ) {
case 0 : { // 2空格缩进
... // 4空格缩进
break ;
}
案例 1 : {
...
break ;
}
default : {
assert (false );
}
}
在单语句循环里,括号可用可不用:
for (int i = 0 ; i < kSomeNumber ; ++ i )
printf (“我爱你\ n ” );
for (int i = 0 ; i < kSomeNumber ; ++ i ) {
printf (“我拿回来\ n ” );
}
空循环体应使用 {} 或 continue,而不是一个简单的分号。
while (condition ) {
//反复循环直到条件失效。
}
for (int i = 0 ; i < kSomeNumber ; ++ i ) {} //可 - 空循环体。
同时 (条件) 继续; //可 - contunue代表没有逻辑。
while (condition ); //差 - 看起来仅仅只是while / loop的部分之一。
9.10。指针和引用表达式
总述
句点或箭头先后不要有空格。指针/地址操做符()以后不能有空格。*, &
说明
下面是指针和引用表达式的正确使用范例:
x = * p ;
p = &x ;
x = r 。y ;
x = r - > y ;
注意:
在访问成员时,句点或箭头先后没有空格。
操做指针符 * 或 & 后没有空格。
在声明指针变量或参数时,星号与类型或变量名紧挨均可以:
//好,空格前置。
char * c ;
const string &str ;
//好,空格后置。
char * c ;
const string & str ;
int x , * y ; //不容许 - 在多重声明中不能使用&或*
char * c ; //差 - *两边都有空格
const string & str ; //差 - &两边都有空格。
在单个文件内要保持风格一致,因此,若是是修改现有文件,要遵守该文件的风格。
9.11。布尔表达式
总述
若是一个布尔表达式超过 标准行宽,断行方式要统一一下。
说明
下例中,逻辑与(&&)操做符总位于行尾:
if (this_one_thing > this_other_thing &&
a_third_thing == a_fourth_thing &&
yet_another && last_one ) {
...
}
注意,上例的逻辑与(&&)操做符均位于行尾。这个格式在Google里很常见,虽然把全部操做符放在开头也能够。能够考虑额外插入圆括号,合理使用的话对加强可读性是颇有帮助的。此外,直接用符号形式的操做符,好比 && 和 ~,不要用词语形式的 and 和 compl。
9.12。函数返回值
总述
在不要 return 表达式里加上非必须的圆括号。
说明
在只有写 要加上括号的时候才在 里使用括号。x = exprreturn expr;
返回 结果; //返回很简单,没有圆括号。
//能够用圆括号把复杂表达式圈起来,改善可读性。
return (some_long_condition &&
another_condition );
回报 (价值); //毕竟您从历来不会写var =(value);
返回(结果); // return可不是函数!
9.13。变量及数组初始化
总述
用 =, () 状语从句: {} 都可。
说明
您能够用 =, () 和 {},如下的例子都是正确的:
int x = 3 ;
int x (3 );
int x { 3 };
字符串 名称(“Some Name” );
字符串 名称 = “某个名称” ;
字符串 名称{ “Some Name” };
请当心务必列表初始化 {...} 用 std::initializer_list 构造函数初始化出的类型。非空列表初始化就会优先调用 std::initializer_list,不过空列表初始化除外,后者原则上会调用默认构造函数。为了强制禁用 std::initializer_list 构造函数,请改用括号。
矢量< INT > v (100 , 1 ); //内容为100个1的向量。
矢量< INT > v { 100 , 1 }; //内容为100和1的向量。
此外,列表初始化不容许整型类型的四舍五入,这能够用来避免一些类型上的编程失误。
int pi (3.14 ); //好 - pi == 3.
int pi { 3.14 }; //编译错误:缩略转换。
9.14。预处理指令
总述
预处理指令不要缩进,从行首开始。
说明
即便预处理指令位于缩进代码块中,指令也应从行首开始。
//好 - 指令从行首开始
if (lopsided_score ) {
#if DISASTER_PENDING //正确 - 从行首开始
DropEverything ();
#if NOTIFY //非必要 - #后跟空格
NotifyClient ();
#endif
#endif
BackToNormal ();
}
//差-指令缩进
若是 (lopsided_score ) {
的#if DISASTER_PENDING //差- “的#if”应该放在行开头
DropEverything ();
#endif //差 - “#endif”不要缩进
BackToNormal ();
}
9.15。类格式
总述
访问控制块的声明依次序是 public:, protected:, private:,每一个都缩进1个空格。
说明
类声明(下面的代码中缺乏注释,参考 类注释)的基本格式以下:
class MyClass : public OtherClass {
public : //注意有一个空格的缩进
MyClass (); //标准的两空格缩进
显式 MyClass (int var );
〜MyClass的() {}
void SomeFunction ();
void SomeFunctionThatDoesNothing () {
}
void set_some_var (int var ) { some_var_ = var ; }
int some_var () const { return some_var_ ; }
private :
bool SomeInternalFunction ();
int some_var_ ;
int some_other_var_ ;
};
注意事项:
全部基类名应在80列限制下尽可能与子类名放在同一行。
关键词 public:, protected:, private: 要缩进1个空格。
除第一个关键词(通常是 public)外,其余关键词前要空一行。若是类比较小的话也能够不空。
这些关键词后不要保留空行。
public 放在最前面,而后是 protected,最后是 private。
声明关于顺序的规则请参考 声明顺序 一节。
9.16。构造函数初始值列表
总述
构造函数初始化列表放在同一行或按四格缩进并排多行。
说明
下面两种初始值列表方式均可以接受:
//若是全部变量能放在同一行:
MyClass :: MyClass (int var ) : some_var_ (var ) {
DoSomething ();
}
//若是不能放在同一行,
//必须置于冒号后,并缩进4个空格
MyClass :: MyClass (int var )
: some_var_ (var ), some_other_var_ (var + 1 ) {
DoSomething ();
}
//若是初始化列表须要置于多行,将每一个成员放在单独的一行
//并逐行对齐
MyClass :: MyClass (int var )
: some_var_ (var ), // 4空格缩进
some_other_var_ (var + 1 ) { //列队
DoSomething ();
}
//右大括号}能够和左大括号{放在同一行
//若是这样作合适的话
MyClass :: MyClass (int var )
: some_var_ (var ) {}
9.17。命名空间格式化
总述
命名空间内容不缩进。
说明
命名空间 不要增长额外的缩进层次,例如:
命名空间 {
void foo () { //正确。命名空间内没有额外的缩进。
...
}
} //命名空间
不要在命名空间内缩进:
命名空间 {
//错,缩进多余了。
void foo () {
...
}
} //命名空间
声明嵌套命名空间时,每一个命名空间都独立成行。
namespace foo {
namespace bar {
9.19。水平留白
总述
水平留白的使用根据在代码中的位置决定。永远不要在行尾添加没意义的留白。
说明
通用
void f (bool b ) { //左大括号前老是有空格。
...
int i = 0 ; //分号前不加空格。
//列表初始化中大括号内的空格是可选的。
//若是加了空格,那么两边都要加。
int x [] = { 0 };
int x [] = { 0 };
//继承与初始列表中的冒号先后恒有空格。
class Foo : public Bar {
public :
//对于单行函数的实现,在大括号内加上空格
//而后是函数实现
Foo (int b ) : Bar (), baz_ (b ) {} //大括号里面是空的话,不加空格。
void Reset () { baz_ = 0 ; } //用括号把大括号与实现分开。
...
添加冗余的留白会给其余人编辑时形成额外负担。所以,行尾不要留空格。若是肯定一行代码已经修改完毕,将多余的空格去掉; 或者在专门清理空格时去掉(尤为是在没有其余人在处理这件事的时候)。(Yang.Y注:如今大部分代码编辑器稍加设置后,都支持自动删除行首/行尾空格,若是不支持,考虑换一款编辑器或IDE)
循环和条件语句
if (b ) { // if条件语句和循环语句关键字后均有空格。
} else { // else先后有空格。
}
while (test ) {} //圆括号内部不紧邻空格。
switch (i ) {
for (int i = 0 ; i < 5 ; ++ i ) {
switch ( i ) { //循环和条件语句的圆括号里能够与空格紧邻。
若是 ( 测试 ) { //圆括号,但这不多见。总之要一致。
for ( int i = 0 ; 我 < 5 ; ++ i ) {
for ( ; i < 5 ; ++ i ) { //循环里内; 后恒有空格,; 前能够加个空格。
switch (i ) {
case 1 : // switch case的冒号前无空格。
...
案例 2 : 打破; //若是冒号有代码,加个空格。
操做符
//赋值运算符先后老是有空格。
x = 0 ;
//其它二元操做符也先后恒有空格,不过对于表达式的子式能够不加空格。
//圆括号内部没有紧邻空格。
v = w * x + y / z ;
v = w * x + y / z ;
v = w * (x + z );
//在参数和一元操做符之间不加空格。
X = - 5 ;
++ x ;
若是 (x && !y )
...
模板和转换
//尖括号(<和>)不与空格紧邻,<前没有空格,>和(之间也没有
矢量< 字符串> X ;
ý = 的static_cast < 炭*> (X );
//在类型与指针操做符之间留空格也能够,但要保持一致。
vector < char *> x ;
9.19。垂直留白
总述
垂直留白越少越好。
说明
这不只仅是规则而是原则问题了:不在仅万不得已,不要使用空行。尤为是:两个函数定义之间的空行不要超过2行,函数体首尾不要留空行,函数体中也不要随意添加空行。
基本原则是:同一屏能够显示的代码越多,越容易理解程序的控制流程。固然,过密集的代码块和过于疏松的代码块一样难看,这取决于你的判断。但一般是垂直留白越少越好。
下面的规则可让加入的空行更有效:有点可读性。
摘录自谷歌开源项目开源指南。