头文件相关
1.4内联函数
定义:
当函数被声明为内联函数之后, 编译器会将其内联展开, 而不是按通常的函数调用机制进行调用.
优点:
只要内联的函数体较小, 内联该函数可以令目标代码更加高效. 对于存取函数以及其它函数体比较短, 性能关键的函数, 鼓励使用内联.
缺点:
滥用内联将导致程序变得更慢. 内联可能使目标代码量或增或减, 这取决于内联函数的大小. 内联非常短小的存取函数通常会减少代码大小, 但内联一个相当大的函数将戏剧性的增加代码大小. 现代处理器由于更好的利用了指令缓存, 小巧的代码往往执行更快。
结论:
一个较为合理的经验准则是, 不要内联超过 10 行的函数. 谨慎对待析构函数, 析构函数往往比其表面看起来要更长, 因为有隐含的成员和基类析构函数被调用!
另一个实用的经验准则: 内联那些包含循环或
switch
语句的函数常常是得不偿失 (除非在大多数情况下, 这些循环或switch
语句从不被执行).有些函数即使声明为内联的也不一定会被编译器内联, 这点很重要; 比如虚函数和递归函数就不会被正常内联. 通常, 递归函数不应该声明成内联函数.(YuleFox 注: 递归调用堆栈的展开并不像循环那么简单, 比如递归层数在编译时可能是未知的, 大多数编译器都不支持内联递归函数). 虚函数内联的主要原因则是想把它的函数体放在类定义内, 为了图个方便, 抑或是当作文档描述其行为, 比如精短的存取函数.
1.5. #include
的路径及顺序
使用标准的头文件包含顺序可增强可读性, 避免隐藏依赖: 相关头文件, 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
的构建会立刻中止。因此这一条规则保证维护这些文件的人们首先看到构建中止的消息而不是维护其他包的人们。
作用域相关
2.1. 命名空间
小技巧
鼓励在 .cc
文件内使用匿名命名空间或 static
声明. 使用具名的命名空间时, 其名称可基于项目名或相对路径. 禁止使用 using 指示(using-directive)。禁止使用内联命名空间(inline namespace)。
定义:
命名空间将全局作用域细分为独立的, 具名的作用域, 可有效防止全局作用域的命名冲突.
优点:
虽然类已经提供了(可嵌套的)命名轴线 (YuleFox 注: 将命名分割在不同类的作用域内), 命名空间在这基础上又封装了一层.
举例来说, 两个不同项目的全局作用域都有一个类
Foo
, 这样在编译或运行时造成冲突. 如果每个项目将代码置于不同命名空间中,project1::Foo
和project2::Foo
作为不同符号自然不会冲突.内联命名空间会自动把内部的标识符放到外层作用域,比如:
namespace X { inline namespace Y { void foo(); } // namespace Y } // namespace X
X::Y::foo()
与X::foo()
彼此可代替。内联命名空间主要用来保持跨版本的 ABI 兼容性。
缺点:
命名空间具有迷惑性, 因为它们使得区分两个相同命名所指代的定义更加困难。
内联命名空间很容易令人迷惑,毕竟其内部的成员不再受其声明所在命名空间的限制。内联命名空间只在大型版本控制里有用。
有时候不得不多次引用某个定义在许多嵌套命名空间里的实体,使用完整的命名空间会导致代码的冗长。
在头文件中使用匿名空间导致违背 C++ 的唯一定义原则 (One Definition Rule (ODR)).
7.4. 变量命名
总述
变量 (包括函数参数) 和数据成员名一律小写, 单词之间用下划线连接. 类的成员变量以下划线结尾, 但结构体的就不用, 如: a_local_variable
, a_struct_data_member
, a_class_data_member_
.
7.6. 函数命名
总述
常规函数使用大小写混合, 取值和设值函数则要求与变量名匹配: MyExcitingFunction()
, MyExcitingMethod()
, my_exciting_member_variable()
, set_my_exciting_member_variable()
.
说明
一般来说, 函数名的每个单词首字母大写 (即 “驼峰变量名” 或 “帕斯卡变量名”), 没有下划线. 对于首字母缩写的单词, 更倾向于将它们视作一个单词进行首字母大写 (例如, 写作 StartRpc()
而非 StartRPC()
).
AddTableEntry() DeleteUrl() OpenFileOrDie()
(同样的命名规则同时适用于类作用域与命名空间作用域的常量, 因为它们是作为 API 的一部分暴露对外的, 因此应当让它们看起来像是一个函数, 因为在这时, 它们实际上是一个对象而非函数的这一事实对外不过是一个无关紧要的实现细节.)
取值和设值函数的命名与变量一致. 一般来说它们的名称与实际的成员变量对应, 但并不强制要求. 例如 int count()
与 void set_count(int count)
.