C++规范
- 类规范
- 构造函数中不调用虚函数,考虑使用Init()或者工厂函数
- 不定义隐式类型转换,对于转换运算符好单参数构造函数,使用explicit
- 如果类中不需要拷贝或者移动,需要在public域中使用
MyClass(const MyClass&) = delete; MyClass& operator=(const MyClass&) = delete;
- 仅有数据成员的使用
struct
,一般用class
- 继承使用
public
。private
的需要直接用组合形式处理 - 尽可能单继承,接口使用
Interface
作为后缀(纯虚函数)没有定义任何构造函数,如果有的话,不能带参数和表示为protected,没有非静态数据成员 - 运算符从中不要乱来,没必要不干
- 将 所有 数据成员声明为 private, 除非是
static const
类型成员
- 函数规范:
- 输入参数在前,输出参数在后
- 函数简单<=40行,若>40不破坏结构进行分割
-
对于引用参数,使用
const
进行约束。(修改变量的值) - 输入参数是值参或
const
引用, 输出参数为指针. 输入参数可以是const
指针, 但决不能是非const
的引用参数, 除非特殊要求, 比如swap()
. - 输入形参用
const T*
满足可能传输空指针,函数要把指针或对地址的引用赋值给形参,应该备注说明使用原因 - 只允许在非虚函数中使用缺省参数, 且必须保证缺省参数的值始终一致.
- 智能指针
-
std::unique_ptr
用来表示动态分配出来的对象的独一无二的所有权,当它离开作用域,对象直接销毁,不能复制,但是可以转移到新的对象,转移所有权 -
std::shared_ptr
表示动态分配独享的所有权,但是可以被共享,可以被复制,对象的所有权由所有复制者共有,只有所有复制者都被销毁,对象才会被销毁。 - 如果必须使用动态分配, 那么更倾向于将所有权保持在分配者手中. 如果其他地方要使用这个对象, 最好传递它的拷贝, 或者传递一个不用改变所有权的指针或引用. 倾向于使用
std::unique_ptr
来明确所有权传递 - 避免高开销拷贝,避免使用
std::shared_ptr
,但是std::shared_ptr<const Foo>
这种操作对象不可变时使用
-
- 右值引用
-
T && k=getVar();
将产生的临时值的生命周期延长到与k同步,利用绑定避免临时对象的拷贝构造和析构,类似于const T&k=getVar()
2.std::move()
,完成将一个左值强制转换为一个右值引用,它对含有资源的对象更有意义,可以提高内存很大的对象的性能,避免深拷贝
-
- 变长数组和alloca()
- 不允许使用变长数组和alloca(),都不合适控制内存与资源分配问题
- 建议使用安全的分配器,
std::vector
,std::unique_ptr<T[]>
- 友元
- 友元定义在同一个文件内,如果允许另一个类访问类的使用成员时,考虑使用友元。
- 异常:不使用C++异常
- 类型转换,使用C++的类型转换,
static_cast<>()
,不使用int y=(int)x;或者int y =int(x)
- 流:只在记录日志中使用流,一般情况使用
printf()
,scanf()
- 前置自增和自减:不考虑返回值时候,选择
++i
,后置自减需要对i进行一次拷贝,简单数值(非对象)无所谓,对迭代器和模板使用前置
*const
用法,任何可能情况下使用const
,来保证变量值不可篡改- 如果函数不会修改传入的引用或指针类型参数
- 尽可能将函数声明为
const
,访问函数应该总是const,其他不会修改任何数据成员,未调用非const函数,不会返回数据成员非const指针或者引用的函数 - 数据成员在对象构造只会不发生改变
- 整型
- <stdint.h>定义
int16_t,uint32_t,int64_t
在需要确保整型大小适合替代short,unsigned long long
等,一般用int
,合适情况下使用size_t,ptrdiff_t
;不建议使用无符号整型,除非确认类型只会是无符号类型数值
- <stdint.h>定义
- 预处理宏
- 不要在 .h 文件中定义宏.
- 在马上要使用时才进行
#define
, 使用后要立即 #undef. - 不要只是对已经存在的宏使用
#undef
,选择一个不会冲突的名称; - 不要试图使用展开后会导致 C++ 构造不稳定的宏, 不然也至少要附上文档说明其行为.
- 不要用
##
处理函数,类和变量的名字。
- auto
auto s1=a; //创建a的拷贝
const auto&s2=a; //s2是a的一个引用
- auto 只能在局部变量中使用,不能在文件作用域,命名空间作用域,类数据成员中使用,不能列表初始化auto变量
- 列表初始化 { }
- 如果构造函数是显示的时候(explicit),不能用={}
- Lambdas
- 按 format 小用 lambda 表达式怡情。
- 禁用默认捕获,捕获都要显式写出来。打比方,比起
[=](int x) {return x + n;}
, 您该写成[n](int x) {return x + n;}
才对,这样读者也好一眼看出 n 是被捕获的值。 - 匿名函数始终要简短,如果函数体超过了五行,那么还不如起名(acgtyrant 注:即把 lambda 表达式赋值给对象),或改用函数。
- 如果可读性更好,就显式写出 lambd 的尾置返回类型,就像auto.
- 不用使用复杂的模板编程
- 命名规范
- 描述性
- 文件名:小写,下划线
_
或者连接符-
- 类型命名: 每个单词首字母大写,不包含下划线
- 变量名和数据成员一律小写,单词用下划线连接,类的成员变量以下划线结尾,结构体不用
- 声明为const或者constexpr的变量,或者在运行期间值保持不变的,命名以
k
开头,大小写混合const int kDaysInAWeek = 7
- 函数命名,驼峰法,取值和设置函数的命名与变量一致,如
int count();void set_count();
- 命名空间小写,最高取决于项目名称
- 枚举,使用常量的命名规则或者宏的命名规则,kDaysInAWeek 或者DAYS_IN_A_WEEK
- 注释风格
- 使用
/ /
或者/* */
- 每一个文件开头加入版权公告,文件注释描述了该文件的内容. 如果一个文件只声明, 或实现, 或测试了一个对象, 并且这个对象已经在它的声明处进行了详细的注释, 那么就没必要再加上文件注释. 除此之外的其他文件都需要文件注释.
- 如果一个 .h 文件声明了多个概念, 则文件注释应当对文件的内容做一个大致的说明, 同时说明各概念之间的联系. 一个一到两行的文件注释就足够了, 对于每个概念的详细文档应当放在各个概念中, 而不是文件注释中.
- 不要在 .h 和 .cc 之间复制注释, 这样的注释偏离了注释的实际意义.
- 每个类的定义,应该附带一份注释,描述类的功能和用法,除非功能相当明显,可以用小段代码演示,如果类的声明和定义分开了,此时, 描述类用法的注释应当和接口定义放在一起, 描述类的操作和实现的注释应当和实现放在一起.
- 函数声明
- 函数的输入输出.
- 对类成员函数而言: 函数调用期间对象是否需要保持引用参数, 是否会释放这些参数.
- 函数是否分配了必须由调用者释放的空间.
- 参数是否可以为空指针.
- 是否存在函数使用上的性能隐患.
- 如果函数是可重入的, 其同步前提是什么?
- 变量可以接受NULL或者 -1等警戒值时候,需要说明
- 类数据成员,说明用途
- 全局变量的用途和含义需要说明
- 巧妙或复杂的代码段前加注释
- 隐晦的地方在行尾空两格进行注释
- 使用
- 格式
- 行长度字符数<=80,除了include
- 使用UTF-8编码
- 只使用空格,每次缩进两个空格
- 注意所有情况下 if 和左圆括号间都有个空格. 右圆括号和左大括号之间也要有个空格
- 空循环体应使用 {} 或 continue, 而不是一个简单的分号.
- 在访问成员时, 句点或箭头前后没有空格.指针操作符 * 或 & 后没有空格.在声明指针变量或参数时, 星号与类型或变量名紧挨都可以
- 如果一个布尔表达式超过 标准行宽, 断行方式要统一.直接使用符号形式的操作符,
&&
或者~
,不是and
或者compl
- 不在return 表达式,加上没必要的圆括号
-
访问控制块的声明依次序是 public:, protected:, private:, 每个都缩进 1 个空格.
- public 放在最前面, 然后是 protected, 最后是 private.
- 命名空间内容不缩进.
网友评论