读谷歌编程规范

一、头文件
每个.cxx(.c/.cc/.cpp)都对应一个.h文件

1、#define 保护
#define 防止头文件被多重包含,命名格式:__FILE>_H_

2、头文件依赖
应该尽量少的包含头文件

3、内联函数
只有当函数只有10行甚至更少时,才会将其定义为内联函数

4、函数参数顺序
定义函数时,参数顺序为:输入参数在前,输出参数在后

5、包含文件的名称及次序
将包含次序标准化,可增强可读性、避免隐藏依赖。次序:C库、C++库、其他库的.h、项目内的.h

项目内头文件应按照源代码目录树结构排列,并且避免使用UNIX文件路径 .(当前路径) 和 ..(父目录)。例如,work_nc/MyProject/CompileOnline/http/http.h,应像这样被包含:
#include “http/http.h”

work/CompileWork.cpp中包含头文件的次序如下:
work/CompileWork.h(优先位置)
C系统文件
C++系统文件
其他库头文件
本项目内头文件

如:
#include “CompileWork.h”

#include

#include
#include

#include “http/http.h”
#include “log/log.h”

二、作用域
1、局部变量
将函数变量尽可能置于最小作用域内,在声明变量时将其初始化。
C++允许在函数任何位置声明变量(C不是)。提倡在尽可能小的作用域中声明变量,离第一次使用越近越好。

int i;
i = f(); //坏—-初始化和声明分离
int i = g();//好 —- 初始化是声明

注:如果变量是一个对象,每次进入作用域都要调用其构造函数,每次退出作用域都要调用其析构函数。

//低效的实现:
for(int i = 0; i < 1000000; i++)
{
Foo f; //构造函数和析构函数分别调用1000000次
f.DoSomething(i);
}

类似变量放到循环外声明要高效的多:
Foo f; //构造函数和析构函数分别调用1次
for(int i = 0; i < 1000000; i++)
{
f.DoSomething(i);
}

2、全局变量
class类型的全局变量是被禁止的(因为构造函数、析构函数、初始化函数的调用顺序是没有严格规定的),内置类型的全局变量是允许的,多线程代码中非常数全局变量也是被禁止的。

对于全局的字符串常量,使用C风格的字符串,而不要使用STL的字符串:
const char teacherName[] = “xxh”;

3、尽量不用全局函数和全局变量,考虑作用域和命名空间限制,尽量单独形成编译单元

三、C++类
1、构造函数(Constructor)职责
构造函数中只进行那些没有实际意义(简单初始化对于程序执行没有实际的逻辑意义,因为成员变量的“有意义”的值大多不在构造函数中确定)的初始化,可能的话,使用Init()方法集中初始化为有意义的数据。

定义:在构造函数中执行初始化操作
优点:排版方便,无需担心类是否初始化
缺点:在构造函数中执行操作引起的问题有:
(1)构造函数中不易报告错误,不能使用异常
(2)操作失败会造成对象初始化失败,引起不确定状态
(3)构造函数内调用虚函数,调用不会派发到子类中实现,即使当前没有子类化实现,将来仍是隐患
(4)如果有人创建该类型的全局变量,构造函数将在main()之前被调用,有可能破坏构造函数中暗含的假设条件。

2、默认构造函数(Default Constructors)
如果一个类定义了若干成员变量又没有其他构造函数,需要定义一个默认构造函数,否则编译器将自动生成默认构造函数,但编译器生成的构造函数并不会对对象进行初始化。
如果你定义的类继承现有类,而你又没有增加新的成员变量,则不需要为新类定义默认构造函数。(echo注:还是添加一个默认构造函数吧,因为我们不确定编译器会为我们做些什么事)

PS:还记得默认构造函数的两种形式吗?

3、明确的构造函数(Explicit Constructors)
通常,只有一个参数的构造函数可被用于转换(echo注:此时称为转换构造函数)。如:定义了
Foo::Foo(string name);
则可使用:
Foo f = “xxh”;
此时的”xxh”会调用Foo(string name)构造函数,如果不想如此转换赋值,使用关键字explicit显示声明。

PS:还记得转换构造函数的两种形式吗?

4、拷贝构造函数(Copy Constructors)
Q:写一个空类时(只有类名,没有成员变量和方法),编译器会为我们做什么?
原则:尽量知晓编译器为我们所做的多余事情,尽量不要让编译器为我们做事,因为这些事情,也许就是你不想要的。

大量的类并不需要拷贝构造函数(copy),也不需要赋值操作(assignment),因此,在确认我们的类不需要时,主动给他们声明为private访问权限

5、结构体和类(Structs vs Classes)
仅当只有数据时使用struct,其他一概使用class。

Q1:C++结构体和类的区别?

Q2:C结构体和C++结构体的区别?

6、继承(Inheritance)
使用组合(composition)通常比使用继承更适宜,(为什么?),如果使用继承的话,只使用公共继承。
原则:如果两个类的关系是 is-a,则使用继承,如:人是动物
如果两个类的关系是 has-a,则使用组合,如:汽车有个发动机

在使用继承时,如果父类有虚函数,谨记,其析构函数也应设为虚析构函数。(为什么?)

Q1:还记得继承的两大特性吗?
(扩展和代码重用)

7、多重继承
避免使用多重继承。
在使用多重继承时,如果不同的子类都是继承来自一个相同的超类,请使用虚继承,如:
class CA
{
};
class CB : virtual public CA
{
};
class CC : virtual public CA
{
};
class CD : public CB, public CC
{
public:
CD() : CB(), CC(), CA() {};
}

PS:Objective-C 是没有这种特性的。

8、接口(Interface)
注:在C++中没有Interface关键字(其他的面向对象语言里面有,如Java)
C++中使用虚类来实现接口的功能:
(1)只有纯虚函数(Q:纯虚那是的定义?以及它的含义?)和静态函数。
(2)没有非静态数据成员。
(3)没有定义任何构造函数,如果有,也不含参数,并且为protected
虚类没有方法的实现,也不能被实例化。它的子类必须要实现虚类的纯虚函数。
(虚类的命名,以字母 I 为前缀)

9、操作符重载(Operator Overloading)
如果非必须,最好不要重载操作符,尽量使用函数代替操作符。

10、声明次序
(1)public: 、protected: 、private: ,成员函数在数据成员之前。
(2)typdef或enums、常量、构造函数、析构函数、成员函数或静态成员函数、数据成员或静态数据成员
相应的 .cxx(cpp,cc)文件函数的顺序与 .h 文件中函数的顺序一致。

11、编写短小函数
不要编写过长代码的函数,如果代码超过40行时,尽量分割成多个函数。
(优点?你懂得)

四、指针
(1)声明指针时,就应该对其进行初始化,如果没有明确的指向时,初始化为NULL
(2)指针作为函数参数时,在函数的入口处就要对指针进行判断
(3)在申请(new)内存时,下一步就要写,这个指针(内存)在何处释放。
(4)在释放(delete)指针后,把指针变量指向NULL
(5)作为函数参数时,尽量使用引用替代指针。
(6)在使用指针(或数组)时,时刻注意释放越界。

五、其他C++特性
(1)引用参数
输入参数为值或常数引用,输出参数为指针,如:
void Foo(const string &in, string *out)

(2)缺省参数(带默认参数的函数)
尽量或禁止使用带有默认参数的函数。

(3)异常
谨慎使用异常。
在调用函数时,谨记判断函数的返回值,根据不同的返回值,作相应的操作流程。

(4)类型转换(Casting)
如需要类型转换时,请使用C++风格(static_cast/const_cast/reinterpret_cast/dynamic_cast),而不要使用C风格,如:int x = int(y)或 int x= (int)y;

(5)流(Streams)
只在记录日志时使用流,其他场景下,使用printf/fprintf/scanf…替代

(6)前缀自增(自减)
对于非内置类型的自增(自减),而且不需要表达式的值时,尽量使用前缀自增(自减),因为前缀自增(自减)比后缀自增(自减)效率高。

Q:能理解 ++i 与 i++ 么?

(7)const的使用
在代码目标明确的情况下,请尽量使用const。

Q1,:还记得const与指针结合的使用吗?

Q2:const string& CA::Fun(const string &in) const{}; 这里的每个const的含义是?

(8)整型
注:C++中类型大小因编译器和体系结构(CPU)的不同而不同。
请使用C中中的int16_t、uint32_t、int64_t等整形替代short、unsigned int、long long等。
不要使用uint32_t等无符号整型(除非你明确知道你在干嘛)

(9)预处理
谨慎使用预处理,尽量使用内联函数、枚举和常量代之

(15)0 和 NULL
整数用 0, 实数用 0.0 ,指针用 NULL,字符(串)用 ‘\0’

(16)sizeof
尽可能使用 sizeof(varname) 代替 sizeof(type)

六、命名约定
规则:通过名字,就能看出:类型、变量、函数、常量、宏等。

(1)通用命名规则
函数名、变量名、文件命名应具有描述性,不要过度缩写,类型和变量应该是名字,函数名可以用“命令性”动词。
如:
int num_errors; // Good.
int num_completed_connections; // Good

int n; // Bad
int nerr; // Bad
int n_comp_conns // Bad ambiguous abbreviation

(2)文件命令
文件名要全部小写,可以包含下划线(_)或短线(-),按项目约定来。
文件名尽量不要与库文件名重名
尽量让文件名更明确,文件名一般与该文件的功能/类相关联
.h 文件与 .cxx(.cc/cpp)文件名一致

可接受的文件命名:
my_useful_class.c
my-useful-class.c
myusrfulclass.c
【请不要使用中文字符】

(3)类型命名
类型命名每个单词以大写字母开头,不包含下划线:MyWork、RequestHttp…
所有类型命名—类、结构体、类型定义(typedef)、枚举—使用相同约定

(4)变量命名
变量名一律小写,单词间以下划线相连,类成员变量以下划线结尾(有些是以下划线开头)
普通变量:
string table_name; // OK
string tablename; // OK
string tableName; // Bad

结构体数据成员与普通变量一样:
struct UrlTableProperties {
string name;
int num_entries;
};

全局变量
尽量少用全局变量。可以g_作为全局变量的前缀。

(5)常量命名
在名称前面在前缀k,后接大写字母:
const int kDaysInAWeek = 7;

(6)函数命名
普通函数:
函数名以大写字母开头,每个单词首字母大写,没有下划线。
void ParseHttpHead();
void DeleteUrl();

存取函数
存取函数与存取变量名匹配,如:
class MyClass
{
public:

int num_entries() const { return num_entries_;}
void set_num_entries(int num_entries) { num_entries_ = num_entries;}
private:
int num_entries_;
};

(7)命名空间
命名空间的名称均用小写

(8)枚举命名
枚举名称,大小写混合,枚举值全部大写,单词间用下划线相连,如:
enum UrlTableErrors {
OK = 0,
ERROR_OUT_OF_MEMORY,
};

(9)宏
宏的命名与枚举值命名一致,均用大写,单词间用下划线隔开

七、代码注释
命名规范的代码本身就是一个说明文档。

(1)注释风格
使用//和/* */均可

(2)文件注释
在每个文件开头加入版权公告,然后是文件内容描述:
1)版权,如:Copyright 2015 echo
2)许可版本
3)作者信息
文件内容:
对文件功能的简要描述

(3)类注释
描述类的功能和用法

(4)函数注释
函数声明处注释描述函数功能,定义处描述函数实现

函数声明注释内容:
1)输入、输出
2)如果函数分配空间,需要由调用者释放
3)函数是否可为NULL
4)是否存在性能隐患

(5)TODO注释
对那些临时、短期的解决方案,或已经够好但并不完美的代码使用TODO注释

八、格式
良好的格式,使阅读代码更容易

(1)行长度
每一行代码字符数尽量不超过80
尽量不要使用非ASCII字符,使用时必须使用UTF-8格式

(2)空格还是制表符
只使用空格,每次缩进2个空格
使用空格进行缩进,不要在代码中使用 tabs,设定编辑器将tab转为空格

(3)函数声明与定义
1)返回值总是和函数名在同一行
2)左圆括号总是和函数名在同一行
3)函数名和左圆括号间没有空格
4)圆括号与参数间没有空格
5)函数声明和实现处的所有参数名称保持一致
6)所有形参尽可能对齐

(4)条件语句
不在圆括号中添加空格,关键字else另起一行,注意大括号的使用。

(5)循环和开关选择语句
switch语句可以使用大括号分块;空循环体应使用{}或continue;

(6)指针和引用表达式
句点(.)或箭头(->)前后不要有空格,指针、地址操作符(*、&)后不要有空格。

(7)函数返回值
return 表达式中不要使用圆括号:
return x;// not return (x)

PS:还Objective-c中一般使用的是圆括号

(8)预处理
预处理指令不要缩进,从首行开始,即使预处理指令位于缩进代码块中。

(9)命名空间格式化
命名空间内容不缩进

不同的公司,不同的部门、组都有自己的编码风格,我们做的是,尽量使代码的风格保持一致,尽量让代码看上去简单易懂。
(完)

发表评论