5.10. 前置自增和自减
对于迭代器和其他模板对象使用前缀形式 (++i) 的自增, 自减运算符.
定义:
对于变量在自增 (++i 或 i++) 或自减 (--i 或 i--) 后表达式的值又没有没用到的情况下, 需要确定到底是使用前置还是后置的自增 (自减).
优点:
不考虑返回值的话, 前置自增 (++i) 通常要比后置自增 (i++) 效率更高. 因为后置自增 (或自减) 需要对表达式的值 i 进行一次拷贝. 如果 i 是迭代器或其他非数值类型, 拷贝的代价是比较大的. 既然两种自增方式实现的功能一样, 为什么不总是使用前置自增呢?
缺点:
在 C 开发中, 当表达式的值未被使用时, 传统的做法是使用后置自增, 特别是在 for 循环中. 有些人觉得后置自增更加易懂, 因为这很像自然语言, 主语 (i) 在谓语动词 (++) 前.
结论:
对简单数值 (非对象), 两种都无所谓. 对迭代器和模板类型, 使用前置自增 (自减).
5.11. const 的使用
我们强烈建议你在任何可能的情况下都要使用 const.
定义:
在声明的变量或参数前加上关键字 const 用于指明变量值不可被篡改 (如 const int foo ). 为类中的函数加上 const 限定符表明该函数不会修改类成员变量的状态 (如 class Foo { int Bar(char c) const; };).
优点:
大家更容易理解如何使用变量. 编译器可以更好地进行类型检测, 相应地, 也能生成更好的代码. 人们对编写正确的代码更加自信, 因为他们知道所调用的函数被限定了能或不能修改变量值. 即使是在无锁的多线程编程中, 人们也知道什么样的函数是安全的.
缺点:
const是入侵性的: 如果你向一个函数传入 const 变量, 函数原型声明中也必须对应 const 参数 (否则变量需要 const_cast 类型转换), 在调用库函数时显得尤其麻烦.
结论:
const 变量, 数据成员, 函数和参数为编译时类型检测增加了一层保障; 便于尽早发现错误. 因此, 我们强烈建议在任何可能的情况下使用 const:
如果函数不会修改传入的引用或指针类型参数, 该参数应声明为 const.
尽可能将函数声明为 const. 访问函数应该总是 const. 其他不会修改任何数据成员, 未调用非 const 函数, 不会返回数据成员非 const 指针或引用的函数也应该声明成 const.
如果数据成员在对象构造之后不再发生变化, 可将其定义为 const.
然而, 也不要发了疯似的使用 const. 像 const int * const * const x; 就有些过了, 虽然它非常精确的描述了常量 x. 关注真正有帮助意义的信息: 前面的例子写成 const int** x 就够了.
关键字 mutable 可以使用, 但是在多线程中是不安全的, 使用时首先要考虑线程安全.
const 的位置:
有人喜欢 int const *foo 形式, 不喜欢 const int* foo, 他们认为前者更一致因此可读性也更好: 遵循了 const 总位于其描述的对象之后的原则. 但是一致性原则不适用于此, “不要过度使用” 的声明可以取消大部分你原本想保持的一致性. 将 const 放在前面才更易读, 因为在自然语言中形容词 (const) 是在名词 (int) 之前.
这是说, 我们提倡但不强制 const 在前. 但要保持代码的一致性! (yospaly 注: 也就是不要在一些地方把 const 写在类型前面, 在其他地方又写在后面, 确定一种写法, 然后保持一致.)
5.12. 整型
C++ 内建整型中, 仅使用 int. 如果程序中需要不同大小的变量, 可以使用 <stdint.h> 中长度精确的整型, 如 int16_t.
5.15. 0 和 NULL
整数用 0, 实数用 0.0, 指针用 NULL, 字符 (串) 用 '\0'.
整数用 0, 实数用 0.0, 这一点是毫无争议的.
对于指针 (地址值), 到底是用 0 还是 NULL, Bjarne Stroustrup 建议使用最原始的 0. 我们建议使用看上去像是指针的 NULL, 事实上一些 C++ 编译器 (如 gcc 4.1.0) 对 NULL 进行了特殊的定义, 可以给出有用的警告信息, 尤其是 sizeof(NULL) 和 sizeof(0) 不相等的情况.
字符 (串) 用 '\0', 不仅类型正确而且可读性好.
命名约定
6.1. 通用命名规则
函数命名, 变量命名, 文件命名应具备描述性; 不要过度缩写. 类型和变量应该是名词, 函数名可以用 “命令性” 动词
如何命名:
尽可能给出描述性的名称. 不要节约行空间, 让别人很快理解你的代码更重要. 好的命名风格:
int num_errors; // Good.
int num_completed_connections; // Good.
糟糕的命名使用含糊的缩写或随意的字符:
int n; // Bad - meaningless.
int nerr; // Bad - ambiguous abbreviation.
int n_comp_conns; // Bad - ambiguous abbreviation.
类型和变量名一般为名词: 如 FileOpener, num_errors.
函数名通常是指令性的 (确切的说它们应该是命令), 如 OpenFile(), set_num_errors(). 取值函数是个特例 (在 函数命名 处详细阐述), 函数名和它要取值的变量同名.
6.2. 文件命名
文件名要全部小写, 可以包含下划线 (_) 或连字符 (-). 按项目约定来.
6.3. 类型命名
类型名称的每个单词首字母均大写, 不包含下划线: MyExcitingClass, MyExcitingEnum
6.4. 变量命名
变量名一律小写, 单词之间用下划线连接. 类的成员变量以下划线结尾
6.5. 常量命名
在名称前加 k: kDaysInAWeek.
所有编译时常量, 无论是局部的, 全局的还是类中的, 和其他变量稍微区别一下. k 后接大写字母开头的单词::
const int kDaysInAWeek = 7;
注释
关于注释风格,很多 C++ 的 coders 更喜欢行注释, C coders 或许对块注释依然情有独钟, 或者在文件头大段大段的注释时使用块注释;
文件注释可以炫耀你的成就, 也是为了捅了篓子别人可以找你;
注释要言简意赅, 不要拖沓冗余, 复杂的东西简单化和简单的东西复杂化都是要被鄙视的;
对于 Chinese coders 来说, 用英文注释还是用中文注释, it is a problem, 但不管怎样, 注释是为了让别人看懂, 难道是为了炫耀编程语言之外的你的母语或外语水平吗;
注释不要太乱, 适当的缩进才会让人乐意看. 但也没有必要规定注释从第几列开始 (我自己写代码的时候总喜欢这样), UNIX/LINUX 下还可以约定是使用 tab 还是 space, 个人倾向于 space;
TODO 很不错, 有时候, 注释确实是为了标记一些未完成的或完成的不尽如人意的地方, 这样一搜索, 就知道还有哪些活要干, 日志都省了.
格式
8.4. 函数声明与定义
返回类型和函数名在同一行, 参数也尽量放在同一行.
函数看上去像这样:
ReturnType ClassName::FunctionName(Type par_name1, Type par_name2) {
DoSomething();
...
}
如果同一行文本太多, 放不下所有参数:
ReturnType ClassName::ReallyLongFunctionName(Type par_name1,
Type par_name2,
Type par_name3) {
DoSomething();
...
}
甚至连第一个参数都放不下:
ReturnType LongClassName::ReallyReallyReallyLongFunctionName(
Type par_name1, // 4 space indent
Type par_name2,
Type par_name3) {
DoSomething(); // 2 space indent
...
}
注意以下几点:
· 返回值总是和函数名在同一行;
· 左圆括号总是和函数名在同一行;
· 函数名和左圆括号间没有空格;
· 圆括号与参数间没有空格;
· 左大括号总在最后一个参数同一行的末尾处;
· 右大括号总是单独位于函数最后一行;
· 右圆括号和左大括号间总是有一个空格;
· 函数声明和实现处的所有形参名称必须保持一致;
· 所有形参应尽可能对齐;
· 缺省缩进为 2 个空格;
· 换行后的参数保持 4 个空格的缩进;