一、头文件
- #define的保护
保证唯一性,头文件命名基于项目源路径代码树的全路径,防止头文件被多重包含,命名方式:< PROJECT > _ < PTAH > _ < FILE > _ H_
eg:foo/src/bar/baz.h
#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_
...
#endif // FOO_BAR_BAZ_H_
- 函数参数顺序
定义函数,参数顺序为:输入参数在前,输出参数在后,输入参数一般传值或常数引用,输出参数或者输入/输出参数为非常数指针。 - 包含文件的名称及次序
包含次序如下:C库,C++库,其他库的.h,项目内的.h。
举例来说,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"
二、命名空间
三、类
- 构造函数的职责
构造函数中只进行没有实际意义的初始化,使用Init()方法集中初始化有意义的数据。 - 默认构造函数
如果一个类定义了若干成员变量又没有其他构造函数,需要定义一个默认构造函数。因为如果我们没有提供自定义默认构造函数,编译器自动给我生成一个,编译器生成的构造函数并不会对对象进行初始化。 - 明确的构造函数
在面向对象编程中,构造参数的隐式转换是指编译器在某些情况下自动将传递给类构造函数的实参(Argument)从一种类型转换为另一种类型,以便能够调用相应的构造函数。这种隐式转换通常涉及单参数构造函数(Single-Argument Constructor),该构造函数可用于从一种类型转换为目标类的对象。
未避免构造函数被调用造成隐式转换,将其声明为explicit。所有单参数构造函数必须是明确的,将关键字explicit加到单参数构造函数前:explicit Foo(string name);
- 拷贝构造函数(无)
- 结构体和类
当只有数据时使用struct,其他一概使用class。 - 继承
如果使用继承的话,只使用公共继承。
继承主要用于两种场合:
1.实现继承,子类继承父类的实现代码;
2.接口继承,子类仅继承父类的方法名称;
子类不能重写父类的非虚函数,原因是非虚函数不能被重写,因为它们的调用在编译时已经确定,而多态时运行时绑定。
努力做到是在“是一个”的情况下使用继承:如果Bar确实是一种Foo,才令Bar是Foo的子类。
必要的话,令析构函数为 virtual,必要是指,如果该类具有虚函数,其析构函数应该为虚
函数
当重定义派生的虚函数时,在派生类中明确声明其为virtual。根本原因在于:如果遗漏virtual,阅读者需要检索类的所有祖先以确认该函数是否为虚函数。
- 多重继承
多重继承允许子类拥有多个基类,要将作为纯接口的基类和具有实现的基类区别开来。当且仅当只有当最多一个基类中含有实现,其他基类都是以interface为后缀的纯接口类时才会使用多重继承。 - 接口(不可 实例化)
当类满足以下要求时,称为纯接口:
1)只有纯虚函数和静态函数;
2)没有非静态数据成员;
3)没有定义任何构造函数。如果有,也不含参数,并且为protected;
4)如果是子类,也只能继承满足上诉条件并以Interface为后缀的类。 - 存取控制
将数据成员私有化,并提供相关存取函数。如定义变量foo_及取值函数foo()、赋值函数set_foo()。 - 声明次序
定义次序如下:public、protected、private。
每一块中,声明次序一般如下:
- typedefs 和enums;
- 常量;
- 构造函数;
- 析构函数;
- 成员函数,含静态成员函数;
- 数据成员,含静态数据成员;
.cc文件中函数的定义尽可能和声明次序一致。
- 编写短小函数
如果函数超过40行,考虑在不影响程序结构的情况下将其分割以下。
四、Google特有的风情
五、其他C++特性
六、命名约定
- 通用命名规则
尽可能给出描述性的名称,不要节约空间,让别人很快理解你的代码比较重要,
好的命名选择:
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
函数名通常是指令性的,如OpenFile(),set_num_errors(),访问函数需要描述的更细致,要与其访问的变量相吻合。
记住除非放到项目外也非常明了,否则不要使用缩写。
int error_count; // Good.
int error_cnt; // Bad.
- 文件命名
文件名(尽可能明确)一定要全部小写,可以包含下划线(_)或短线(-),按项目约定来。
可接受的文件命名:
my_useful_class.cc
my-useful-class.ccc
myusefulclass.cc - 类型命名
类型命名每个单词以大写字母开头,不包含下划线:MyExcitingClass。
所有类型命名–类、结构体、类型定义(typedef)、枚举–使用相同约定,例如:
// classes and structs
class UrlTable { ...
class UrlTableTester { ...
struct UrlTableProperties { ...
// typedefs
typedef hash_map<UrlTableProperties *, string> PropertiesMap;
// enums
enum UrlTableErrors { ...
- 变量命名
变量名一律小写,单词间以下划线相连,类的成员变量以下划线结尾,如my_exciting_local_variable、my_exciting_member_variable_。
普通变量命名:
string table_name; // OK - uses underscore.
string tablename; // OK - all lowercase.
string tableName; // Bad - mixed case
类数据成员:
结构体的数据成员可以和普通变量一样,不用像类那样接下划线:
struct UrlTableProperties {
string name;
int num_entries;
}
全局变量:
可以用g_区别局部变量区分的标志为前缀。
- 常量命名
在名称前加k:kDaysInAWeek。
常量定义k后接大写字母开头的单词:
const int kDaysInAWeek = 7;
- 函数命名
普通函数大小写混合
函数名以大写字母开头,每个单词首字母大写,没有下划线:
AddTableEntry()
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_;
};
- 枚举命名
枚举值应全部大写,单词间以下划线相连:MY_EXCITING_ENUM_VALUE。
枚举名称属于类型,因此大小写混合:UrlTableErrors。
enum UrlTableErrors {
OK = 0,
ERROR_OUT_OF_MEMORY,
ERROR_MALFORMED_INPUT,
};