这个Google C++风格指南出得太好了,有很多C++的问题,其实通过阅读这份文档就可以了。相信读完后,可以在简历上加上一句,“具有良好的编码风格”,哈哈。下面记录一下我的读书笔记吧。整份文档的中文版本我已经上传到了资源里面。
1. 头文件
1.1头文件保护
每次Eclipse CDT新建一个Class的时候,都是做了define保护:
所有头文件都应该使用 #define 防止头文件被多重包含, 命名格式当是:
<PROJECT>_<PATH>_<FILE>_H_
1.2头文件依赖
能用前置声明的地方尽量不使用 #include.
当一个头文件被包含的同时也引入了新的依赖, 一旦该头文件被修改, 代码就会被重新编译.
如果这个头文件又包含了其他头文件, 这些头文件的任何改变都将导致所有包含了该头文件的
代码被重新编译. 因此, 我们倾向于减少包含头文件, 尤其是在头文件中包含头文件。
使用前置声明可以显著减少需要包含的头文件数量. 举例说明: 如果头文件中用到类 File,
但不需要访问 File 类的声明, 头文件中只需前置声明 class File; 而无须 #include
"file/base/file.h".
不允许访问类的定义的前提下, 我们在一个头文件中能对类 Foo 做哪些操作?
? 我们可以将数据成员类型声明为 Foo * 或 Foo &.
? 我们可以将函数参数 / 返回值的类型声明为 Foo (但不能定义实现).
? 我们可以将静态数据成员的类型声明为 Foo, 因为静态数据成员的定义在类定义之外
.
反之, 如果你的类是 Foo 的子类, 或者含有类型为 Foo 的非静态数据成员, 则必须包含 Foo
所在的头文件.
1.3. 内联函数
一个较为合理的经验准则是, 不要内联超过 10 行的函数. 谨慎对待析构函数, 析构函数往往
比其表面看起来要更长, 因为有隐含的成员和基类析构函数被调用!
2.作用域
2.5. 静态和全局变量
禁止使用 class 类型的静态或全局变量: 它们会导致很难发现的 bug 和不确定的构
造和析构函数调用顺序. 静态生存周期的对象, 包括全局变量, 静态变量, 静态类成员变量,
以及函数静态变量, 都必须是原生数据类型 (POD : Plain Old Data)。
3类
3.4. 拷贝构造函数
缺点:
C++ 中的隐式对象拷贝是很多性能问题和 bug 的根源. 拷贝构造函数降低了代码可读性, 相
比传引用, 跟踪传值的对象更加困难, 对象修改的地方变得难以捉摸.
可以考虑在类的 private: 中添加拷贝构造函数和赋值操作的空实现, 只有声明, 没有定义.
由于这些空函数声明为 private, 当其他代码试图使用它们的时候, 编译器将报错. 方便起见
, 我们可以使用 DISALLOW_COPY_AND_ASSIGN 宏:
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&)
class Foo {
public:
Foo(int f);
~Foo();
private:
DISALLOW_COPY_AND_ASSIGN(Foo);
};
如果类确实需要可拷贝, 应在该类的头文件中说明原由, 并合理的定义拷贝构造函数和赋值操
作。
5.其它C++特性
5.7. 运行时类型识别
优点:
RTTI 在某些单元测试中非常有用. 比如进行工厂类测试时, 用来验证一个新建对象是否为期
望的动态类型.
除测试外, 极少用到.
缺点:
在运行时判断类型通常意味着设计问题. 如果你需要在运行期间确定一个对象的类型, 这通常
说明你需要考虑重新设计你的类.
5.10. 前置自增和自减
优点:
不考虑返回值的话, 前置自增 (++i) 通常要比后置自增 (i++) 效率更高. 因为后置自增 (或
自减) 需要对表达式的值 i 进行一次拷贝. 如果 i 是迭代器或其他非数值类型, 拷贝的代价
是比较大的. 既然两种自增方式实现的功能一样, 为什么不总是使用前置自增呢?
结论:
对简单数值 (非对象), 两种都无所谓. 对迭代器和模板类型, 使用前置自增 (自减).