c++ 规范总结
前言
根据
c++代码规范1.1
整理
头文件
- 建议:每一个.cpp文件都要对应一个.h头文件
- 头文件必须使用#define保护,统一包含它所需要的其它头文件,也不要求定义任何特别符号,不要使用
#pragma once
-
define保护的命名格式为
<PROJECT>_<PATH>_<FILE>_H_
- 建议:尽量避免使用前置声明,使用
#include
包含需要的头文件即可 - 只有当函数只有10行甚至更少时才将其定义为内联函数
- include顺序
dir2/foo2.h
(相关头文件)- C 系统文件
- C++ 系统文件
- 其他库的
.h
文件- 本项目内
.h
文件
作用域
- 禁止使用 using 指示(using-directive)
- 禁止使用内联命名空间(inline namespace)
- 禁止在命名空间
std
内声明 - 禁止在头文件中使用命名空间别名
- 在命名空间的最后注释出命名空间的名字
- 命名空间中声明和定义不要使用缩进
- 匿名命名空间的声明和命名空间的格式相同,在最后注释上
namespace
,所有置于匿名命名空间的声明都具有内部链接性 - 建议:非成员函数应尽量置于某个命名空间内或使用静态成员函数
- 建议:尽可能小的作用域中声明变量, 离第一次使用越近越好,并在变量声明时初始化
- 建议:循环语句内的变量尽量在外部声明
- 全局变量,静态变量,静态类成员变量和函数静态变量,都必须是原生数据类型, 以及 POD 类型的指针、数组和结构体。
类
-
禁止在构造函数中调用虚函数
-
禁止在无法报出错误时进行可能失败的初始化,考虑使用
Init()
方法或使用工厂模式 -
禁止在构造函数中抛出异常
-
不要定义隐式类型转换
-
如果需要就让你的类型可拷贝, 需要同时给出拷贝构造函数和赋值操作符的定义
-
类型不需要拷贝, 需要将拷贝构造函数和赋值运算符禁用。
-
仅当只有数据成员时使用
struct
, 其它一概使用class
-
所有继承必须是
public
的 -
如果基类有虚函数, 则析构函数也应该为虚函数
-
所有数据成员都是私有的
-
只有当所有父类除第一个外都是 纯接口类 时, 才允许使用多重继承
-
除少数特定环境外, 不要重载运算符. 也不要创建用户定义字面量
-
类定义一般应以
public
开始, 后跟protected
, 最后是private
-
建议:类中声明顺序为类型 (包括
typedef
,using
和嵌套的结构体与类), 常量, 工厂函数, 构造函数, 赋值运算符, 析构函数, 其它函数, 数据成员
函数
- 建议:函数的参数顺序为: 输入参数在先, 后跟输出参数
- 输入参数通常是值参或
const
引用, 输出参数或输入/输出参数则一般为非const
指针 - 输出参数禁止使用引用
- 建议:函数尽量简短
- 引用参数必须是const
- 输入参数使用const指针,需要说明原因
- 禁止虚函数使用缺省参数
- 指针检查
其他c++特性
- 我们自己的代码中不要抛出异常,但是需要抓取第三方库的异常。捕获的异常需要处理,错误异常要输出日志
- 除了项目中第三方库需要,我们禁止使用 RTTI
- 不要使用流, 除非是日志接口需要. 使用
printf
之类的代替 - 统一使用前置自增 (自减)
- 强烈建议你在任何可能的情况下都要使用const
- 使用<stdint.h>中长度精确的整形
- 只在一个文件中使用的宏要在.cc文件中定义不要定义在头文件中
- 指针变量定义时要初始化,指针销毁后要置
nullptr
,指针使用前要进行有效性判断 - 指针使用前要使用
if (nullptr == p)
或if (nullptr == p)
进行防错处理 - 指针初始化用
nullptr
- 用
sizeof(varname)
代替sizeof(type)
- 禁止使用复杂的模板编程,参数不要超过3个.
- 不要使用C++11,除非项目中第三方库需要
命名约定
- 命名要有一致性
- 文件名要全部小写, 可以包含下划线,对于
MFC
项目中自动生成的文件名可不遵循本规则 - 类, 结构体, 类型定义 (
typedef
), 枚举命名单词首字母均大写, 不包含下划线
命名规则 类型+描述
类型
C :类
Struct : 结构体
P : typedef/using 指针类型
En: 枚举
- 变量命名采用匈牙利命名法
- 函数命名采用帕斯卡命名法
- const变量用const前缀
- 命名空间命名全部小写,嵌套命名空间要注意冲突
- 枚举类型使用En前缀,枚举值命名全大写ENUM_前缀_
- _宏命名全大写下划线_分割
注释
- 文件头,函数/类说明使用/* /
- *其他使用
//
或/* */
, 统一就好 - 每个文件开头加入文件注释,包括版权、说明、作者等信息,文件注释应当对文件的内容做一个大致的说明, 同时说明各概念之间的联系
- 每个类的定义都要附带一份注释, 描述类的功能和用法,如果该类的实例可被多线程访问, 要特别注意文档说明多线程环境下相关的规则和常量使用,用一小段代码演示这个类的基本用法或通常用法
- 类的声明和定义分开了(例如分别放在了
.h
和.cc
文件中), 此时, 描述类用法的注释应当和接口定义放在一起, 描述类的操作和实现的注释应当和实现放在一起 - 函数的注释声明处描述函数功能; 定义处的注释描述函数实现;函数声明处注释函数的输入输出,参数是否可以为空等,函数定义处主要注释函数如何实现
- 变量注释,变量名不足以说明用途,如果变量可以接受
NULL
或-1
等警戒值, 须加以说明;全局变量注释全局的原因 - 复杂代码前添加注释,行注释空2格
- TODO注释用来临时的, 短期的解决方案, 或已经够好但仍不完美的代码,为了在 "将来某一天做某事。格式使用全大写的字符串
TODO
, 在随后的圆括号里写上你的名字, 邮件地址, bug ID - 弃用注释标记某接口为弃用状态,格式全大写的
DEPRECATED
在括号中留下您的名字, 邮箱地址以及其他身份标识 - 修改他人注释格式//name 日期,修改原因
格式
- 建议:每一行代码字符数不超过 80
- 只使用空格, 每次缩进 2 个空格
- 函数返回值和函数名保持同一行,形参放不下可以分行
- 函数调用要么在圆括号里对参数分行, 要么参数另起一行且缩进四格
- 条件语句中不要在圆括号内使用空格. 关键字
if
和else
另起一行,如果有else if 在最后要加一个 else - 开关语句中case语句使用大括号,应该总是包含一个
default
匹配,如果default
应该永远执行不到, 简单的加条assert
,两条case
之间没有break
需要注释加以说明 - 句点或箭头前后不要有空格. 指针/地址操作符 (
*, &
) 之后不能有空格 - 声明指针变量或参数时, 星号与变量名紧挨
==
与常量比较时常量要写在前面- 布尔表达式中逻辑与 (
&&
) 操作符总位于行头 - 只有在写
x = expr
要加上括号的时候才在return expr;
里使用括号 - 禁用
std::initializer_list
构造函数 - 预处理指令不要缩进, 从行首开始,即使预处理指令位于缩进代码块中, 指令也应从行首开始
- 访问控制块的声明依次序是
public:
,protected:
,private:
, 不缩进 - 命名空间内容不缩进
- 不要使用
#pragma once
ADD
- 禁止使用魔数,使用sizeof、define
- if else注释