2012年(3)
分类: C/C++
2012-06-14 21:36:34
背景:
C++是Google许多开源项目所主要采用的开发语言。众所周知,C++拥有许多强大的特性,但这也造就了C++高度的复杂性,以至于使得C++编码更易出错以及较难阅读。
本手册编写的目的正是去尝试通过约定代码中“要做”和“不要做的”规范来解决这种复杂性。使得代码既可以依然使用到语言强大的特性,又易于管理。
代码风格,也叫“代码可读性”,是我们规范我们代码的准则。但其实“风格”这个词并不够精准,因为这些原则所涵盖的内容远远不仅是源代码的格式化。
保证代码易于管理的其中一个行之有效的方法就是强制要求代码保持“一致性”。让一名程序员可以快速的阅读和理解另外一人所写的代码是非常重要的。保持统一的代码风格和遵循一致的代码编写规范意味着我们可以更容易的实现所谓“模式匹配”以使我们从大量的变量中快速明白他们是什么以及他们有哪些共同之处。
发布这篇文章的另外一个考虑是我们所说的“C++膨胀”。C++是一门拥有众多特性的庞大的语言。然而在一些场景下,出于保持代码简介和避免更多的共通性错误,我们不得不被约束甚至是禁止使用部分这些特性。这份文档罗列出了被禁止使用的这些特性并解释了为什么他们被禁止使用。
NOTE:这份文档并不是一份C++指引,我们假设这份文档的读者已经十分熟悉这门语言。
Header Files
一般说来,每一个.cc的文件都关联着一个.h文件。但这也有一些例外,比如单元测试文件或者一些小的只包含main函数的.cc文件。
改善头文件的使用方法能对你代码的可读性、大小、表现带来重大的改观。如下的规则会引导你如何正确的使用头文件并绕过一些陷阱:
所有的头文件都应该包含#define警戒常量(#define guard)以避免重复嵌套的问题。警戒常量的声明应遵循如下格式:
<项目名/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_
应当充分使用“预声明”以最少的#include头文件。
当你include了一个头文件的时候,你相当于对你的代码引入了一个依赖性,这意味着每当任何一个头文件被改变,你的执行代码也将需要被重新编译一次。同样,如果你的头文件也包括了其他的头文件,那任何对这些文件的改变也将引起代码的重编译。因此,我们应当最小化include的次数,特别是在头文件中include其他头文件。
使用“预声明”可以显著的减少文件包含的次数。举例来说,如果你的头文件需要声明一个File类型的变量但并没有真正的去使用它,那么你可以仅在你的头文件中声明一句:class File;而不需要去#include “file/base/file.h”
那么我们该如何在头文件中使用Foo类而不需要访问它的定义(definition)呢?
但是如果头文件中定义的类是派生自Foo或者需要有一个类别为Foo的成员变量(非指针类型),你将必须引用Foo类别的头文件。
针对这个问题,一般我们可以通过使用该类别的指针而非其直接的对象成员已解决。但这将就一定的可能性会导致代码复杂度的增加或者效率的下降,因此,如果你仅仅是因为想要降低include的次数而这么做时,不要这么做。
当然,.cc文件中必须要包含对应的若干个头文件。
当你的头文件声明中需要有大量的inline函数的时候,将这些inline函数的定义放入-inl.h结尾的头文件中而不是.cc文件中。
函数参数的顺序
当定义一个函数的时候,参数的顺序应该为:输入->输出。
C++的函数参数要么呢是给函数指定了输入,要么是函数的输出,要么既是输入也是输出。一般来说,作为输入的参数应该是const引用,以确保在函数运行时不会对其值进行改变,而输出参数则是非const指针。当对参数顺序进行排序的时候,应当将所有输入参数放在输出参数之前。特别指出的是,不要仅仅因为一个参数是新加的就将新参数放在参数表的最后,应当将其按照上述原则放在合适的位置。
当然,这不是一个强制性的规则,同样对效率上也没有什么提升,仅为了增加代码可读性而已。
头文件的命名和包含顺序
为了提升可读性以及避免隐含的依赖性,应按照“C库函数,C++库函数,其他库函数,你自己的头文件”这样的顺序对头文件进行include