c++规范
# c++规范
Owner: -QVQ-
# C++规范
# 头文件
通常每个 .cpp
文件应该有一个关联的 .h
文件
- 所有头文件必须有多重保护
#ifndef #define #endif
, 符号名称格式_<PROJECT>_<PATH>_<FILE>_H_
- 头文件中不使用前置声明,直接
#include
所需的其他头文件 - 只有当函数只有10行甚至更少时才将其定义为内联函数.
- 函数参数顺序:输入参数在前,输出参数在后
- 函数多返回值:多返回值时,只在函数返回值中直接返回执行结果或错误码,其他数据通过输出参数返回
- 使用标准的头文件包含顺序可增强可读性,避免隐藏依赖,避免使用UNIX特殊的快捷目录.或..
- 包含次序:C库、C++库、其他库的.h、项目内的.h
# 作用域
- 嵌套类符合局部使用原则,只是不能在其他头文件中前置声明,尽量不要public;
- 尽量不用全局函数和全局变量,考虑作用域和命名空间限制,尽量单独形成编译单元;
- 多线程中的全局变量(含静态成员变量)不要使用class类型(含STL容器),避免不明确行为导致的 bug.
- 作用域的使用,除了考虑名称污染,可读性之外,主要是为降低耦合,提高编译/执行效率
- 注意「using指示(using-directive)」和「using 声明(using-declaration)」的区别。
- 匿名命名空间说白了就是文件作用域,就像Cstatic声明的作用域一样,后者已经被C++标准提倡弃用。
- 局部变量在声明的同时进行显式值初始化,比起隐式初始化再赋值的两步过程要高效,
- 注意别在循环犯大量构造和析构的低级错误。
- 命名空间:鼓励在.cc文件内使用匿名命名空间或static声明。提倡使用不具名命名空间,基于项目或者路径名称。命名空间内不用缩进。避免使用 using 提示符污染命名空间
- 在.cc文件中定义一个不需要被外部引用的变量时,可以将它们放在匿名命名空间或声明为static 。但是不要在.h文件中这么做。
# 类
不要在构造函数中调用虚函数,或做太多逻辑相关的初始化,编译器默认构造函数不会初始化变量
不要定义义隐式类型转换.对于转换运算符和单参数构造函数,请使用explicit关键字.
如果你的类型需要,就让它们支持拷贝/移动.否则,就把隐式产生的拷贝和移动函数禁用,可声明为 private 且无需实现
仅在作为数据集合时使用 struct,其他一律clas
组合>实现继承>接口继承>私有继承,子类重载的虚函数也要声明 virtual 关键词
多重继承唯一的使用场景是最多一个基类含实现,其他基类纯接口
接口类类名以 Interface 为后缀,全部使用纯虚函数
当一个类满足以下要求时,称之为纯接口:
• 只有纯虚函数(”=0”)和静态函数(除了下文提到的析构函数).
• 没有非静态数据成员.
• 没有定义任何构造函数.如果有,也不能带有参数,并且必须为protected.
• 如果它是一个子类,也只能从满足上述条件并以Interface为后缀的类继承
除少数特定环境外,不要重载运算符
将所有数据成员声明为private,除非是static const类型成员(遵循常量命名规则).。允许测试固件类中的数据成员为protected.
声明次序:public -> protected -> private 每行缩进一个空格
# 函数
- 我们倾向于按值返回,否则按引用返回。避免返回指针,除非它可以为空
- 我们倾向于编写简短,凝练的函数
- 所有按引用传递的参数必须加上const.
- 只允许在非虚函数中使用缺省参数,且必须保证缺省参数的值始终一致,优先用重载
- 只有在常规写法(返回类型前置)不便于书写或不便于阅读时使用返回类型后置语法(
auto foo(int x) → int
) - 只在定义移动构造函数与移动赋值操作时使用右值引用.不要使用std::forward
- 不允许使用变长数组和alloca和异常
- 不要使用c风格的类型转换,使用c++风格的类型转化
- 只在记录日志时使用流,其他用printf()和scanf().
- 一般使用++i前缀的自增
- 在任何可能的情况下用const和constexpr
- 使用宏时要非常谨慎,尽量以内联函数,枚举和常量代替之.
- 整数用0,实数用0.0,指针用nullptr或NULL,字符(串)用'\0'.
- 用auto绕过烦琐的类型名,只要可读性好就继续用,别用在局部变量之外的地方。
- 适当使用lambda表达式。别用默认lambda捕获,所有捕获都要显式写出来
- 只使用Boost中被认可的库
# 命名规则
- 文件名要全部小写,可以包含下划线(_),通常应尽量让文件名更加明确
my_useful_class.cc
- 类型命名:每个单词首字母大写,不包含下划线
MyExcitingClass
- 变量命名:均小写,包含下划线(_);
table_name
类的私有成员以下划线结尾table_name_
结构体变量和普通变量一样命名 - **常量命名:**以“k”开头,大小写混合,
const int kDaysInAWeek = 7;
- 全局变量:以
g_
为前缀 - 静态变量:以
s_
为前缀 - 函数命名:大 小 写 混 合, 取 值 和 设 值 函 数 则 要 求 与 变 量 名 匹 配
MyExcitingFunction()
set_my_exciting_member_variable()
. - 命名空间命名:全部小写,基于项目名和目录结构
- 枚举命名:枚举值和枚举名都用驼峰
enum UrlTableErrors {
kOK = 0,
kErrorOutOfMemory,
kErrorMalformedInput,
};
1
2
3
4
5
2
3
4
5
宏命名:大写和”_“
注释:说明代码功能的关键注释统一使用
/**/
- 文件注释使用 Doxygen 风格
- 函数注释:函数声明处的注释描述函数功能;定义处的注释描述函数实现.
- 变量注释:上一行使用
//
每个类数据成员都应该注释说明 - 每个类带注释
// Iterates over the contents of a GargantuanTable. // Example: // GargantuanTableIterator* iter = table->NewIterator(); // for (iter->Seek("foo"); !iter->done(); iter->Next()){ // process(iter->key(), iter->value()); // } // delete iter for (iter->Seek("foo"); !iter->done(); iter->Next()) { process(iter->key(), iter->value()); } delete iter; class GargantuanTableIterator { ... };
1
2
3
4
5
6
7
8
9
10
11
12
13
14语法
- TODO:临时方案使用 TODO 注释
- 代码格式:一个 TAB=4个空格
- 1行长度:字符数不超过80
- 括号、空格、换行:
- 操作符之间空格隔开
- 关键字与括号之间留一个空格
- do while 的 while 与花括号在同一行
- for 循环分号后面加空格
- 非ASCII字符:使用 UTF-8
上次更新: 2025/02/21, 14:57:10