没有合适的资源?快使用搜索试试~ 我知道了~
资源推荐
资源详情
资源评论
Google
Google
Google
Google C++
C++
C++
C++ 编程风格指南(一)
背景
背景
背景
背景
Google 的开源项目大多使用 C++ 开发。每一个 C++ 程序员也都知道, C++ 具有很多强大的
语言特性,但这种强大不可避免的导致它的复杂,这种复杂会使得代码更易于出现 bug 、难于
阅读和维护。
本指南的目的是通过详细阐述在 C++ 编码时要怎样写、不要怎样写来规避其复杂性。这些规 则
可在允许代码有效使用 C++ 语言特性的同时使其易于管理。
风格,也被视为可读性,主要指称管理 C++ 代码的习惯。使用术语风格有点用词不当,因为 这
些习惯远不止源代码文件格式这么简单。
使代码易于管理的方法之一是增强代码一致性, 让别人可以读懂你的代码是很重要的, 保持统 一
编程风格意味着可以轻松根据 “ 模式匹配 ” 规则推断各种符号的含义。 创建通用的、 必需的习惯 用
语和模式可以使代码更加容易理解, 在某些情况下改变一些编程风格可能会是好的选择, 但我 们
还是应该遵循一致性原则,尽量不这样去做。
本指南的另一个观点是 C++ 特性的臃肿。 C++ 是一门包含大量高级特性的巨型语言, 某些情 况
下, 我们会限制甚至禁止使用某些特性使代码简化,避免可能导致的各种问题,指南中列举了 这
类特性,并解释说为什么这些特性是被限制使用的。
由 Google 开发的开源项目将遵照本指南约定。
注意:本指南并非 C++ 教程,我们假定读者已经对 C++ 非常熟悉。
头文件
头文件
头文件
头文件
通常,每一个 .cc 文件( C++ 的源文件)都有一个对应的 .h 文件(头文件),也有一些例外,
如单元测试代码和只包含 main() 的 .cc 文件。
正确使用头文件可令代码在可读性、文件大小和性能上大为改观。
下面的规则将引导你规避使用头文件时的各种麻烦。
1.
1.
1.
1. #define
#define
#define
#define 的保护
的保护
的保护
的保护
所有头文件都应该使用 #define 防止头文件被
多重包含 (
multiple inclusion
)
, 命名格式当
是:
<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_
2.
2.
2.
2. 头文件依赖
头文件依赖
头文件依赖
头文件依赖
使用 前置声明( forward declarations ) 尽量减少 .h 文件中 #include 的数量。
当一个头文件被包含的同时也引入了一项新的 依赖( dependency ) ,只要该头文件被修改,
代码就要重新编译。 如果你的头文件包含了其他头文件, 这些头文件的任何改变也将导致那些 包
含了你的头文件的代码重新编译。因此, 我们宁可尽量少包含头文件, 尤其是那些包含在其他 头
文件中的。
使用前置声明可以显著减少需要包含的头文件数量。举例说明: 头文件中用到类 File ,但不需 要
访问 File 的声明,则头文件中只需前置声明 class File; 无需 #include
"file/base/file.h" 。
在头文件如何做到使用类 Foo 而无需访问类的定义?
1) 将数据成员类型声明为 Foo * 或 Foo & ;
2) 参数、返回值类型为 Foo 的函数只是声明(但不定义实现);
3) 静态数据成员的类型可以被声明为 Foo ,因为静态数据成员的定义在类定义之外。
另一方面,如果你的类是 Foo 的子类,或者含有类型为 Foo 的非静态数据成员,则必须为之 包
含头文件。
有时, 使用指针成员 ( pointer members , 如果是 scoped_ptr 更好 ) 替代对象成员 ( obj ect
members )的确更有意义。然而,这样的做法会降低代码可读性及执行效率。如果仅仅为了 少
包含头文件,还是不要这样替代的好。
当然, .cc 文件无论如何都需要所使用类的定义部分,自然也就会包含若干头文件。
译者注:能依赖声明的就不要依赖定义。
3.
3.
3.
3. 内联函数
内联函数
内联函数
内联函数
只有当函数只有 10 行甚至更少时才会将其定义为 内联函数( inline function ) 。
定义( Definition
Definition
Definition
Definition ): 当函数被声明为内联函数之后,编译器可能会将其内联展开,无需按通
常的函数调用机制调用内联函数。
优点:
当函数体比较小的时候, 内联该函数可以令目标代码更加高效。 对于
存取函数 (
access or
、
mutator ) 以及其他一些比较短的关键执行函数。
缺点: 滥用内联将导致程序变慢,内联有可能是目标代码量或增或减, 这取决于被内联的函数 的
大小。内联较短小的存取函数通常会减少代码量,但内联一个很大的函数(译者注:如果编译 器
允许的话)将戏剧性的增加代码量。在现代处理器上,由于更好的利用 指令缓存( instruction
cache ) ,小巧的代码往往执行更快。
结论: 一个比较得当的处理规则是,不要内联超过 10 行的函数。对于析构函数应慎重对待, 析
构函数往往比其表面看起来要长,因为有一些隐式成员和基类析构函数(如果有的话)被调用!
另一有用的处理规则:内联那些包含循环或 switch 语句的函数是得不偿失的,除非在大多数 情
况下,这些循环或 switch 语句从不执行。
重要的是,虚函数和递归函数即使被声明为内联的也不一定就是内联函数。 通常, 递归函数不 应
该被声明为内联的( 译者注:递归调用堆栈的展开并不像循环那么简单, 比如递归层数在编译 时
可能是未知的,大多数编译器都不支持内联递归函数 ) 。 析构函数内联的主要原因是其定义在 类
的定义中,为了方便抑或是对其行为给出文档。
4.
4.
4.
4. -inl.h
-inl.h
-inl.h
-inl.h 文件
文件
文件
文件
复杂的内联函数的定义,应放在后缀名为 -inl.h 的头文件中。
在头文件中给出内联函数的定义,可令编译器将其在调用处内联展开。 然而, 实现代码应完全 放
到 .cc 文件中, 我们不希望 .h 文件中出现太多实现代码, 除非这样做在可读性和效率上有明显 优
势。
如果内联函数的定义比较短小、逻辑比较简单,其实现代码可以放在 .h 文件中。例如,存取函
数的实现理所当然都放在类定义中。出于实现和调用的方便,较复杂的内联函数也可以放到 .h
文件中,如果你觉得这样会使头文件显得笨重,还可以将其分离到单独的 -inl.h 中。这样即把 实
现和类定义分离开来,当需要时包含实现所在的 -inl.h 即可。
-inl.h 文件还可用于函数模板的定义,从而使得模板定义可读性增强。
要提醒的一点是, -inl.h 和其他头文件一样,也需要 #define 保护。
5.
5.
5.
5. 函数参数顺序(
函数参数顺序(
函数参数顺序(
函数参数顺序( Function
Function
Function
Function Parameter
Parameter
Parameter
Parameter Ordering
Ordering
Ordering
Ordering )
)
)
)
定义函数时,参数顺序为:输入参数在前,输出参数在后。
C/C++ 函数参数分为输入参数和输出参数两种, 有时输入参数也会输出 (译者注: 值被修改时
)。
输入参数一般传值或 常数引用( const references
)
,输出参数或输入 / 输出参数为 非常数指 针
( non-const pointers ) 。对参数排序时,将所有输入参数置于输出参数之前。不要仅仅因为
是新添加的参数,就将其置于最后,而应该依然置于输出参数之前。
这一点并不是必须遵循的规则,输入 / 输出两用参数(通常是类 / 结构体变量)混在其中,会使 得
规则难以遵循。
6.
6.
6.
6. 包含文件的名称及次序
包含文件的名称及次序
包含文件的名称及次序
包含文件的名称及次序
将包含次序标准化可增强可读性、避免 隐藏依赖( hidden dependencies ,译者注:隐藏依 赖
主要是指包含的文件中编译时) ,次序如下: C 库、 C++ 库、其他库的 .h 、项目内的 .h 。
项目内头文件应按照项目源代码目录树结构排列,并且避免使用 UNIX 文件路径 .
(当前目录)
和 .. (父目录)。例如, google-awesome-project/src/base/logging.h 应像这样被包含:
#include "base/logging.h"
dir/foo.cc 的主要作用是执行或测试 dir2/foo2.h 的功能, foo.cc 中包含头文件的次序如下:
dir2/foo2.h (优先位置,详情如下)
C 系统文件
C++ 系统文件
其他库头文件
本项目内头文件
这种排序方式可有效减少隐藏依赖, 我们希望每一个头文件独立编译。 最简单的实现方式是将 其
作为第一个 .h 文件包含在对应的 .cc 中。
dir/foo.cc 和 dir2/foo2.h 通常位于相同目录下(像 base/basictypes_unittest.cc 和
base/basictypes.h ),但也可在不同目录下。
相同目录下头文件按字母序是不错的选择。
举例来说, google-awesome-project/src/foo/internal/fooserver.cc 的包含次序如下:
#include "foo/public/fooserver.h" // 优先位置
#include <sys/types.h>
#include <unistd.h>
#include <hash_map>
#include <vector>
#include "base/basictypes.h"
#include "base/commandlineflags.h"
#include "foo/public/bar.h"
______________________________________
译者:英语不太好,翻译的也就不太好。这一篇主要提到的是头文件的一些规则,总结一下:
1.
1.
1.
1. 避免多重包含是学编程时最基本的要求;
2.
2.
2.
2. 前置声明是为了降低编译依赖,防止修改一个头文件引发多米诺效应;
3.
3.
3.
3. 内联函数的合理使用可提高代码执行效率;
4.
4.
4.
4. -inl.h
-inl.h
-inl.h
-inl.h 可提高代码可读性(一般用不到吧 :D
:D
:D
:D );
5.
5.
5.
5. 标准化函数参数顺序可以提高可读性和易维护性(对函数参数的堆栈空间有轻微影响,我 以
前大多是相同类型放在一起);
6.
6.
6.
6. 包含文件的名称使用 .
.
.
. 和 ..
..
..
.. 虽然方便却易混乱,使用比较完整的项目路径看上去很清晰、 很 条
理,包含文件的次序除了美观之外,最重要的是可以减少隐藏依赖,使每个头文件在 “
“
“
“ 最需要 编
译 ”
”
”
” (对应源文件处 :D
:D
:D
:D )的地方编译,有人提出库文件放在最后,这样出错先是项目内的文件,
头文件都放在对应源文件的最前面,这一点足以保证内部错误的及时发现了。
Google
Google
Google
Google C++
C++
C++
C++ 编程风格指南(二)
作用域
作用域
作用域
作用域
1.
1.
1.
1. 命名空间(
命名空间(
命名空间(
命名空间( Namespaces
Namespaces
Namespaces
Namespaces )
)
)
)
在 .cc 文件中,提倡使用 不具名的命名空间( unnamed namespaces ,译者注:不具名的命 名
空间就像不具名的类一样,似乎被介绍的很少 :-( ) 。使用具名命名空间时,其名称可基于项目
或路径名称,不要使用 using 指示符。
定义:
命名空间将全局作用域细分为不同的、 具名的作用域, 可有效防止全局作用域的命名冲
突。
优点: 命名空间提供了(可嵌套) 命名轴线( name axis ,译者注:将命名分割在不同命名空
间内) ,当然,类也提供了(可嵌套)的命名轴线( 译者注:将命名分割在不同类的作用域内
) 。
举例来说,两个不同项目的全局作用域都有一个类 Foo ,这样在编译或运行时造成冲突。如果
每个项目将代码置于不同命名空间中, project1::Foo 和 project2::Foo 作为不同符号自然不
会冲突。
缺点: 命名空间具有迷惑性,因为它们和类一样提供了额外的(可嵌套的)命名轴线。在头文 件
中使用不具名的空间容易违背 C++ 的 唯一定义原则( One Definition Rule (ODR) ) 。
结论: 根据下文将要提到的策略合理使用命名空间。
剩余55页未读,继续阅读
资源评论
ygplusplus
- 粉丝: 0
- 资源: 4
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功