CPP & 笔记 - Google C++ 编程规范
2021/6/26 14:56:59
本文主要是介绍CPP & 笔记 - Google C++ 编程规范,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
头文件
#define
<PROJECT>_<PATH>_FILE_H_
头文件依赖
-
使用 前置声明(forward declarations) 尽量减少 .h 文件中 #include 的数量
-
头文件中用到类 File,但不需要访问 File 的声明
-
则头文件中只需 前置声明 class File;
-
无需 #include "file.h"
-
-
包含头文件的名称及次序
-
名称要求
- 按照项目源代码目录树结构排序,并且避免使用 UNIX 文件路径:.(当前目录)和 …(父目录)
-
次序
-
本项目头文件
-
C 系统文件
-
C++ 系统文件
-
其他库头文件
-
本项目内头文件
-
内联函数(inline function)
-
只有当函数只有 10 行甚至更少时,才会将其定义为 内联函数
-
所有的内联函数的定义应放在 .h 文件中
-
如果内联函数的定义比较短小、逻辑比较简单,其实现代码可以放在 .h 文件中
-
较复杂的内联函数也可以放到 .h 文件中,或将其分离到单独的 -inl.h 中
-
函数参数顺序(Function Parameter Ordering)
-
输入参数在前,输出参数在后
-
输入参数 一般 传值 或 常数引用(const references)
-
输出参数 或 输入/输出参数 为 非常数指针
-
作用域
命名空间
提倡使用 不具名的命名空间(unnamed namespaces)
不要声明命名空间 std 下的任何内容,包括标准库类的 前置声明
不具名命名空间(Unnamed Namespaces)
具名命名空间(Named Namespaces)
命名空间将 **除文件包含、全局标识的声明/定义 以及 类的前置声明** 外的整个源文件封装起来
以同其他命名空间相区分
嵌套类 / 成员类
- 不要将嵌套类定义为 public,除非它们是接口的一部分
非成员函数(Nonmember)、静态成员函数(Static Member)和全局函数(Global Functions)
- 使用命名空间中的 非成员函数 或 静态成员函数,尽量不要使用 全局函数
局部变量
- 将函数变量尽可能置于 最小作用域内,在声明变量时将其初始化
全局变量(Global Variables)
-
禁止使用 class 类型 的全局变量(包括 STL 的 string, vector 等等)
- 一定要使用的话,请使用 单例模式(singleton pattern)
-
全局的 字符串常量,使用 C 风格 的字符串,而不要使用 STL 的字符串
const charkFrogSays[] = "ribbet";
类
构造函数(Constructor)的职责
-
构造函数中只进行那些没有实际意义的(trivial)初始化
-
可能的话,使用 Init() 方法 几种初始化 **有意义的(non-trivial)**数据
默认构造函数(Default Constructors)
-
如果 定义了若干成员变量,但 没有其他构造函数,则自定义一个默认构造函数
- 编译器自动生产的默认构造函数并不会对对象进行初始化
明确的构造函数(Explicit Constructors)
-
对单参数构造函数使用 C++ 关键字 explicit
- 避免隐式转换
拷贝构造函数(Copy Constructors) 和 赋值操作(assignment operator)
-
仅在代码中需要拷贝一个类对象的时候使用拷贝构造函数
-
不需要拷贝时,应使用
DISALLOW_COPY_AND_ASSIGN
-
避免编译器自动生成相应的 public 的拷贝构造函数
-
可能会是导致 性能问题 和 bugs 的根源,并且降低了 代码可读性(相比按引用传递,跟踪按值传递的对象更加困难)
-
#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); };
结构体和类(Structs vs. Classes)
- 仅当只有数据时,使用 struct,其它一概使用 class
- struct 被用在仅包含数据的消极对象(passive objects)
- 可能包括有关联的常量,但没有存取数据成员之外的函数功能
- 而存取功能通过直接访问实现而无需方法调用
- 如:构造函数、析构函数、Initialize()、Reset()、Validate()
- 如果与 STL 结合,对于 仿函数(functors)和特性(traits) 也可使用 struct
- struct 被用在仅包含数据的消极对象(passive objects)
继承(Inheritance)
- 使用 组合(composition)(“is-a”) 通常比使用 继承(“has-a”) 更适宜
- 如果使用继承的话,只使用公共继承
- C++ 实践中,继承主要用于两种场合
- 实现继承(implementation inheritance),子类继承父类的实现代码
- 接口继承(interface inheritance),子类仅继承父类的方法名称
多重继承(Multiple Inheritance)
- 只有当最多一个基类中 含有实现,其他基类都是以 Interface 为后缀的纯接口类时,才会使用多重继承
- 为确保其余基类都是纯接口,必须以 Interface 为后缀
接口(Interface)
- 当一个类满足以下要求时,称之为纯接口
- 只有 纯虚函数("=0") 和 静态函数,析构函数 除外
- 没有非静态数据成员
- 没有定义任何构造函数。如果有,也不含参数,并且为 protected
- 如果是子类,也只能继承满足上述条件并以 Interface 为后缀的类
- 为确保接口类的所有实现可被正确销毁,必须为之声明 虚析构函数
操作符重载(Operator Overloading)
- 除少数特定环境外,不要重载 操作符
- 如果有需要的话,可以定义类似 Equals()、CopyFrom() 等函数
存取控制(Access Control)
- 将数据成员私有化,并提供相关存取函数
- 变量
foo_
、取值函数foo()
、赋值函数set_foo()
- 变量
声明次序(Declaration Order)
- 类中的声明次序:
public:
==>protected:
==>private:
- 每一块中,声明次序如下
- typedefs 和 enums
- 常量
- 构造函数
- 析构函数
- 成员函数,含静态成员函数
- 数据成员,含静态数据成员
- 宏 DISALLOW_COPY_AND_ASSIGN 置于
private:
块之后,作为类的最后部分 - .cc 文件中,函数的定义 应尽可能和 声明次序 一直
智能指针
- 任何情况下都不要使用 auto_ptr,使用 scoped_ptr 或 shared_ptr
其他 C++ 特性
引用参数(Reference Arguments)
-
函数形参表中,所有按引用传递的参数必须加上 const
void Foo(const string &in, string *out);
函数重载(Function Overloading)
- 仅在 输入参数不同、功能相同 时使用重载函数(含构造函数)
- 不要使用函数重载模仿 缺省函数参数
- 如果想要重载一个函数,实现不同的功能,考虑让函数名包含参数信息。如:AppendString() 和 AppendInt()
缺省参数(Default Arguments)
- 禁止使用缺省函数参数
- 所有参数必须在使用时明确指定
变长数组 和 alloca(Variable-Length Arrays and alloca())
- 禁止使用变长数组 和 alloca()
- 使用安全的 分配器(allocator)
友元(Friends)
- 允许合理使用 友元类 及 友元函数
异常(Exceptions)
- 禁止使用 C++ 异常
运行时类型识别(Run-Time Type Information,RTTI)
- 禁止使用 RTTI
- 虚函数 可以实现 随子类类型不同而执行不同代码,工作都是交给对象本身去完成
- 如果工作在对象之外的代码中完成,考虑 双重分发方案,如 Visitor 模式,可以方便的在对象本身之外确定类的类型
类型转换(Casting)
- 使用 static_cat<>() 等 C++ 的类型转换
- static_cast:实现 值的强制转换 或 指针的父类到子类的明确的向上转换
- const_cast:移除 const 属性
- reinterpret_cast:指针类型 和 整型 或 其他指针间 不安全的相互转换
- dynamic_cast:除测试外不要使用,如果需要在运行时确定类型信息,说明设计有缺陷
流(Streams)
- 只有在 记录日志 时使用流
- 流最大的优势是在输出时不需要关心输出对象的类型,这是一个亮点,也是一个不足
- 容易用错类型,而编译器不会报警
- 使用 printf + read/write
前置自增和自减(Preincrement and Predecrement)
- 对于 迭代器 和 其他模板对象 使用前缀形式(++i)的自增、自减运算符
- 对 简单数值(非对象) 都可
const 的使用(Use of const)
- 在任何可以使用的情况下都要使用 const
- 如果函数不会修改传入的 引用 或 指针类型 的参数,这样的参数应该为 const
- 尽可能将 函数 声明为 const,访问函数 应该总是 const
- 其他函数如果 不会修改任何数据成员 也应该是 const,不要调用 非 const 函数,不要返回 对数据成员的非 const 指针 或 引用
- 如果 数据成员 在 对象构造 之后不再改变,可将其定义为 const
整型(Integer Type)
- C++ 内建整型中,只使用 int
- 如果程序中需要不同大小的变量,可以使用 <stdint.h> 中的 精确宽度(precise-width) 的整型,如 int16_t, int64_t
- 禁止使用 uint32_t 等无符号整型
- 如果数值不会为负值,使用 断言(assertion) 来保护数据
- 无符号整型可能会导致一些数值上的 bug,以及 类型提升机制(type-promotion scheme) 会致使无符号类型的行为出乎意料
64 位下的可移植性(64-bit Portability)
- printf() 的部分格式需要使用宏进行自定义
- *sizeof(void ) != sizeof(int)
- 注意 结构对齐
- 创建 64 位常量时,使用 LL 或 ULL 作为后缀,如
int64_t my_value = 0x123456789LL;
uint64_t my_mask = 3ULL << 48
- 如果确实需要 32 位和 64 位系统具有不同代码,可以在代码变量前使用
预处理宏(Preprocessor Macros)
- 尽量以 内联函数、枚举 和 常量 代替之
- 宏的高级应用,参考 C 语言宏的高级应用
- 参考信息
- 不要在 .h 文件中定义宏
- 使用前正确 #define,使用后正确 undef
- 不要只是对已经存在的宏使用 #undef,选择一个不会冲突的名称
- 不适用会导致不稳定的 C++ 构造(unbalanced C++ constructs)的宏
0 和 NULL(0 and NULL)
- 整数用 0,实数用 0.0,指针用 NULL,字符(串)用 ‘\0’
sizeof(sizeof)
- 尽可能使用 sizeof(varname) 代替 sizeof(type)
Boost 库(Boost)
命名约定
通用命名规则(General Naming Rules)
函数命名、变量命名、文件命名应具有描述性,不要过度缩写
类型和变量 应该是名词,函数名 可以用"命令性"动词
文件命名(File Name)
- C++ 文件以 .cc 结尾,头文件以 .h 结尾
- 内联函数放在 .h 文件内,或单独放到以 -inl.h 结尾的文件中
- 文件名全部小写,包含下划线
类型命名(Type Name)
- 类、结构体、类型定义、枚举,都使用此约定
- 每个单词以大写字母开头,不包含下划线
变量命名(Variable Names)
- 类的成员变量以下划线结尾
- 全局变量以 g_ 为前缀
- 变量名一律小写,包含下划线
常量命名(Constant Names)
- 常量以 k 为前缀
- 每个单词以大写字母开头,不包含下划线
函数命名(Function Names)
普通函数(regular functions)
- 每个单词以大写字母开头,不包含下划线
存取函数(accessors and mutators)
- 与变量名匹配 class MyClass { public: int num_entries() { return num_entries; } void set_num_entries(int num_entries) { num_entries_ = num_entries; } private: int num_entries_; };
命名空间
- 命名空间的名称一律小写,包含下划线
枚举命名(Enumerator Names)
枚举名称
- 每个单词以大写字母开头,不包含下划线
枚举值
- 全部大写,单词间以下划线相连
宏命名(Macro Names)
- 全部大写,单词间以下划线相连
格式
行长度(Line Length)
- 每一行代码字符数不超过 80
非 ASCII 字符(Non-ASCII Characters)
- 使用 UTF-8 格式
空格还是制表位(Spaces vs. Tabs)
- 只使用空格
- 每次缩进 2 个空格
函数声明与定义(Functions Declarations and Definitions)
- 返回类型和函数名在同一行,合适的话,参数也放在同一行
- 注意点
- 返回值总是和函数名在同一行;
- 左圆括号(openparenthesis)总是和函数名在同一行;
- 函数名和左圆括号间没有空格;
- 圆括号与参数间没有空格;
- 左大括号(opencurlybrace)总在最后一个参数同一行的末尾处;
- 右大括号(closecurlybrace)总是单独位于函数最后一行;
- 右圆括号(closeparenthesis)和左大括号间总是有一个空格;
- 函数声明和实现处的所有形参名称必须保持一致;
- 所有形参应尽可能对齐;
- 缺省缩进为 2 个空格;
- 独立封装的参数保持 4 个空格的缩进。
函数调用(Functions Calls)
- 尽量放在同一行,否则将实参封装在圆括号中
条件语句(Conditionals)
- 不在圆括号中添加空格,关键字 else 另起一行
- if 和 左圆括号 间有个空格,右圆括号 和 左大括号 间也有个空格
- 当 语句简单 并且没有使用 else 子句 时,可以将 条件语句写在同一行
循环和开关选择语句(Loops and Switch Statements)
- switch 语句中的 default 永不会执行,可以简单的使用 assert:
default: assert(false);
- 空循环体应使用 {} 或 continue
指针和引用表达式
- 句点
.
或 箭头->
前后不要有空格 - 指针
*
或 地址操作符&
后不要有空格
布尔表达式(Boolean Expressions)
- 逻辑与
&&
操作符总位于行尾
函数返回值(Return Values)
- return 表达式中不要使用 圆括号
变量及数组初始化
- = 或者 () 都可
预处理指令(Preprocessor Directives)
- 预处理指令不要缩进,从行首开始
类格式(Class Format)
- 声明属性依次序是
public:
==>protected:
==>private:
,每次缩进 1 个空格- 所以基类名应在 80 列限制下尽量与子类名放在同一行;
- 关键词 public:、 protected:、 private:要缩进 1 个空格
- 除第一个关键词(一般是 public)外,其他关键词前空一行,如果类比较小的话也可以不空;
- 这些关键词后不要空行;
- public 放在最前面,然后是 protected 和 private;
- 关于声明次序参考第三篇声明次序一节
初始化列表(Initializer Lists)
- 构造函数初始化列表放在 同一行 或 四个缩进并排几行
命名空间格式化(Namespace Formatting)
- 命名空间内容不添加额外缩进层次
水平留白
- 不要加入多余的空格
垂直留白
- 垂直留白越少越好
这篇关于CPP & 笔记 - Google C++ 编程规范的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-06-26解决google chrome helper 内存占用较高!
- 2024-04-01got an unexpected keyword argument
- 2024-03-30维多利亚的秘密 golang入坑系统
- 2024-03-29mongodb sort by date
- 2024-03-29go swagger
- 2024-03-25mongodb cdc
- 2024-03-25how to use go in vscode
- 2024-03-22mongooseserverselectionerror: connect econnrefused ::1:27017
- 2024-03-21pymongo insert_many
- 2024-03-18projection mongodb