Google C++编程风格指南(读后感)

https://blog.csdn.net/nightrainljh/article/details/49914073

头文件

通常,每一个.cc 文件(C++的源文件)都有一个对应的.h 文件(头文件),也有一些例外,如单元测试代
码和叧包吨 main()的.cc 文件。

1. #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. 头文件依赖

使用前置声明(forward declarations)尽量减少.h 文件中#include 的数量。头文件中用到类 File,但不需要访问 File
的声明,则头文件中叧需前置声明 class File;无需#include “file/base/file.h”。
在头文件如何做到使用类 Foo 而无需访问类的定义?

  1. 将数据成员类型声明为 Foo *戒 Foo &;
  2. 参数、返回值类型为 Foo 的函数只是声明(但不定义实现);
  3. 静态数据成员的类型可以被声明为 Foo,因为静态数据成员的定义在类定义之外。
    另一方面,如果你的类是 Foo 的子类,或者含有类型为 Foo 的非静态数据成员,则必须为之包含头文件。
    有时,使用指针成员(pointer members,如果是 scoped_ptr 更好)替代对象成员(object members)
    的确更有意义。
    能依赖声明的就不要依赖定义。

3. 内联函数

只有当函数只有10行甚至更少时才会将其定义为内联函数(inline function)。
定义(Definition):当函数被声明为内联函数之后,编译器可能会将其内联展开,无需按通常的函数调用机制调用内联函数。
结论:一个比较得当的处理规则是,不要内联超过10行的函数。对于析构函数应慎重对待,析构函数往往比其表面看起来要长,因为有一些隐式成员和基类析构函数(如果有的话)被调用!
另一有用的处理规则:内联那些包含循环或switch语句的函数是得不偿失的,除非在大多数情况下,这些循环或switch语句从不执行。
重要的是,虚函数和递归函数即使被声明为内联的也不一定就是内联函数。

4. -inl.h文件

复杂的内联函数的定义,应放在后缀名为-inl.h的头文件中。
-inl.h文件还可用于函数模板的定义,从而使得模板定义可读性增强。要提醒的一点是,-inl.h和其他头文件一样,也需要#define保护。

5. 函数参数顺序

定义函数时,参数顺序为:输入参数在前,输出参数在后。
C/C++函数参数分为输入参数和输出参数两种,有时输入参数也会输出(译者注:值被修改时)。输入参数一般传值或常数引用(constreferences),输出参数或输入/输出参数为非常数指针(non-const pointers)。对参数排序时,将所有输入参数置于输出参数之前。

6. 包含文件的名称及次序

将包含次序标准化可增强可读性、避免隐藏依赖,次序如下: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++系统文件
其他库头文件
本项目内头文件
相同目录下头文件按字母序是不错的选择。
举例来说,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. 命名空间(Namespaces)

在.cc文件中,提倡使用不具名的命名空间。使用具名命名空间时,其名称可基于项目或路径名称,不要使用using指示符。
举例来说,两个不同项目的全局作用域都有一个类Foo,这样在编译或运行时造成冲突。如果每个项目将代码置于不同命名空间中,project1::Foo和project2::Foo作为不同符号自然不会冲突。

  1. 不具名命名空间(Unnamed Namespaces)
    在.cc文件中,允许甚至提倡使用不具名命名空间,以避免运行时的命名冲突:
    namespace{ // .cc 文件中
    // 命名空间的内容无需缩进
    enum { UNUSED, EOF, ERROR}; // 经常使用的符号
    bool AtEof() { return pos_ == EOF; } // 使用本命名空间内的符号EOF
    } // namespace
    然而,与特定类关联的文件作用域声明在该类中被声明为类型、静态数据成员或静态成员函数,而不是不具名命名空间的成员。像上文展示的那样,不具名命名空间结束时用注释// namespace标识。
    不能在.h文件中使用不具名命名空间。
  2. 具名命名空间(Named Namespaces)命名空间将除文件包含、全局标识的声明/定义以及类的前置声明外的整个源文件封装起来,以同其他命名空间相区分。
// .h文件
namespace mynamespace {
// 所有声明都置于命名空间中// 注意不要使用缩进
class MyClass {
public:
  ...
  void Foo();
};}  // namespace mynamespace
// .cc文件
namespace mynamespace {// 函数定义都置于命名空间中
void MyClass::Foo() {
  ...
}}  // namespace mynamespace

通常的.cc文件会包含更多、更复杂的细节,包括对其他命名空间中类的引用等。

#include "a.h"
DEFINE_bool(someflag, false, "dummy flag");
class C;  // 全局命名空间中类C的前置声明
namespace a { class A; }  // 命名空间a中的类a::A的前置声明
namespace b {
...code forb...               // b中的代码
}  // namespace b

不要声明命名空间std下的任何内容,包括标准库类的前置声明。声明std下的实体会导致不明确的行为,如,不可移植。声明标准库下的实体,需要包含对应的头文件。

最好不要使用using指示符,以保证命名空间下的所有名称都可以正常使用。

2. 嵌套类(Nested Class)

定义:可以在一个类中定义另一个类,嵌套类也称成员类(member class)。

class Foo {
private:
  // Bar是嵌套在Foo中的成员类
  class Bar {
 ...
  };};

结论:不要将嵌套类定义为public,除非它们是接口的一部分,比如,某个方法使用了这个类的一系列选项。

3. 非成员函数、静态成员函数和全局函数

使用命名空间中的非成员函数或静态成员函数,尽量不要使用全局函数。
定义于同一编译单元的函数,被其他编译单元直接调用可能会引入不必要的耦合和连接依赖;静态成员函数对此尤其敏感。可以考虑提取到新类中,或者将函数置于独立库的命名空间中。
如果你确实需要定义非成员函数,又只是在.cc文件中使用它,可使用不具名命名空间或static关联(如static int Foo() {…})限定其作用域。

4. 局部变量(Local Variables)

将函数变量尽可能置于最小作用域内,在声明变量时将其初始化。
C++允许在函数的任何位置声明变量。我们提倡在尽可能小的作用域中声明变量,离第一次使用越近越好。这使得代码易于阅读,易于定位变量的声明位置、变量类型和初始值。特别是,应使用初始化代替声明+赋值的方式。

5. 全局变量(Global Variables)

class类型的全局变量是被禁止的,内建类型的全局变量是允许的,当然多线程代码中非常数全局变量也是被禁止的。永远不要使用函数返回值初始化全局变量。
因此,禁止使用class类型的全局变量(包括STL的string, vector等等),因为它们的初始化顺序有可能导致构造出现问题。内建类型和由内建类型构成的没有构造函数的结构体可以使用,如果你一定要使用class类型的全局变量,请使用单件模式(singleton pattern)。
对于全局的字符串常量,使用C风格的字符串,而不要使用STL的字符串:
记住,静态成员变量视作全局变量,所以,也不能是class类型!

  1. .cc中的不具名命名空间可避免命名冲突、限定作用域,避免直接使用using提示符污染命名空间;
  2. 嵌套类符合局部使用原则,只是不能在其他头文件中前置声明,尽量不要public;
  3. 尽量不用全局函数和全局变量,考虑作用域和命名空间限制,尽量单独形成编译单元;
  4. 多线程中的全局变量(含静态成员变量)不要使用class类型(含STL容器),避免不明确行为导致的bugs。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值