C/C++代码规范

零、前言


 

  笔者最近在看开源代码,看到代码格式各自良莠不齐,感受像是各家各有所长。所以打算写一篇关于C/C++代码规范文章,请各位参考,并践踏批评。java

1、文件排版


1. 包含头文件

 • 先系统头文件,后用户头文件。
 • 系统头文件,稳定的目录结构,应采用包含子路径方式。
 • 自定义头文件,不稳定目录结构,应在dsp中指定包含路径。
 • 系统头文件应用:#include <xxx.h>
 • 自定义同文件应用:#include "xxx.h"
 • 只引用须要的头文件。程序员

2. h和cpp文件

 • 头文件命名为.h,内联文件命名为.inl;C++文件命名为*.cpp
 • 文件名用大小写混合,或者小写混合。例如DiyMainview.cppinfoview.cpp。不要用无心义的名称:例如XImage.cppSView.cppxlog.cpp
 • 头文件除了特殊状况,应使用#ifdef控制块。
 • 头文件#endif应采用行尾注释。
 • 头文件,首先是包含代码块,其次是宏定义代码块,而后是全局变量,全局常量,类型定义,类定义,内联部分。
 • CPP文件,包含指令,宏定义,全局变量,函数定义。数组

3. 文件结构

 • 文件应包含文件头注释和内容。
 • 函数体类体之间原则上用2个空行,特殊状况下可用一个或者不须要空行。编辑器

4. 空行

 • 文件头、控制块,#include部分、宏定义部分、class部分、全局常量部分、全局变量部分、函数和函数之间,用两个空行。函数

2、注释


1. 文件头注释

 • 做者,文件名称,文件说明,生成日期(可选)优化

2. 函数注释

 • 关键函数必须写上注释,说明函数的用途。
 • 特别函数参数,须要说明参数的目的,由谁负责释放等等。
 • 除了特别状况,注释写在代码以前,不要放到代码行以后。
 • 对每一个#else#endif给出行末注释。
 • 关键代码注释,包括但不限于:赋值,函数调用,表达式,分支等等。
 • 善未实现完整的代码,或者须要进一步优化的代码,应加上 // TODO …
 • 调试的代码,加上注释 // only for DEBUG
 • 须要引发关注的代码,加上注释 // NOTE …
 • 对于较大的代码块结尾,如for,while,do等,可加上 // end for|while|dospa

3、命名


1. 原则

 • 同一性:在编写一个子模块或派生类的时候,要遵循其基类或总体模块的命名风格,保持命名风格在整个模块中的同一性。
 • 标识符组成:标识符采用英文单词或其组合,应当直观且能够拼读,可望文知意,用词应当准确,避免用拼音命名。
 • 最小化长度 && 最大化信息量原则:在保持一个标识符意思明确的同时,应当尽可能缩短其长度。
 • 避免过于类似:不要出现仅靠大小写区分的类似的标识符,例如"i"与"I""function""Function"等等。
 • 避免在不一样级别的做用域中重名:程序中不要出现名字彻底相同的局部变量和全局变量,尽管二者的做用域不一样而不会发生语法错误,但容易令人误解。
 • 正确命名具备互斥意义的标识符:用正确的反义词组命名具备互斥意义的标识符,如:"nMinValue" 和"nMaxValue""GetName()" 和"SetName()" ….
 • 避免名字中出现数字编号:尽可能避免名字中出现数字编号,如Value1,Value2等,除非逻辑上的确须要编号。这是为了防止程序员偷懒,不愿为命名动脑筋而致使产生无心义的名字(由于用数字编号最省事)。设计

2. T,C,M,R类

 • T类表示简单数据类型,不对资源拥有控制权,在析构过程当中没有释放资源动做。
 • C表示从CBase继承的类。该类不能从栈上定义变量,只能从堆上建立。
 • M表示接口类。
 • R是资源类,一般是系统固有类型。除了特殊状况,不该在开发代码中出现R类型。指针

3. 函数名

 • M类的函数名称应采用HandleXXX命名,例如:HandleTimerEvent;不推荐采用java风格,例如handleTimerEvent;除了标准c风格代码,不推荐用下划线,例如,handle_event
 • Leave函数,用后缀L。
 • Leave函数,且进清除栈,用后缀LC。
 • Leave函数,且删除对象,用后缀LD。调试

4. 函数参数

 • 函数参数用a做为前缀。
 • 避免出现和匈牙利混合的命名规则如apBuffer名称。用aBuffer便可。
 • 函数参数比较多时,应考虑用结构代替。
 • 若是不能避免函数参数比较多,应在排版上可考虑每一个参数占用一行,参数名竖向对齐。

5. 成员变量

 • 成员变量用m最为前缀。
 • 避免出现和匈牙利混合的命名规则如mpBuffer名称。用mBuffer便可。

6. 局部变量

 • 循环变量和简单变量采用简单小写字符串便可。例如,int i;
 • 指针变量用p打头,例如void* pBuffer;

7. 全局变量

 • 全局变量用g_最为前缀。

8. 类名

 • 类和对象名应是名词。
 • 实现行为的类成员函数名应是动词。
 • 类的存取和查询成员函数名应是名词或形容词。

9. 风格兼容性

 • 对于移植的或者开源的代码,能够沿用原有风格,不用C++的命名规范。

4、代码风格

1. Tab和空格

 • 每一行开始处的缩进只能用Tab,建议tab用四个空格代替,感受tab8个空格略长不美观。不能用空格,输入内容以后统一用空格。除了最开始的缩进控制用Tab,其余部分为了对齐,须要使用空格进行缩进。这样能够避免在不一样的编辑器下显示不对齐的状况。
 • 在代码行的结尾部分不能出现多余的空格。
 • 不要在"::","->","."先后加空格。
 • 不要在",",";"以前加空格。

2. 类型定义和{

 • 类,结构,枚举,联合:大括号另起一行

3. 函数

 • 函数体的{须要新起一行,在{以前不能有缩进。
 • 除了特别状况,函数体内不能出现两个空行。
 • 除了特别状况,函数体内不能宏定义指令。
 • 在一个函数体内,逻揖上密切相关的语句之间不加空行,其它地方应加空行分隔。
 • 在头文件定义的inline函数,函数之间能够不用空行,推荐用一个空行。

4. 代码块

 • "if"、"for"、"while"、"do"、"try"、"catch" 等语句自占一行,执行语句不得紧跟其后。不论执行语句有多少都要加 “{ }” 。这样能够防止书写和修改代码时出现失误。
 • "if"、"for"、"while"、"do"、"try"、"catch" 的括号和表达式,括号可紧挨关键字,这样强调的是表达式。

5. else

• if语句若是有else语句,用 } else { 编写为一行,不推荐用 3 行代码的方式。

6. 代码行

 • 一行代码只作一件事情,如只定义一个变量,或只写一条语句。这样的代码容易阅读,而且方便于写注释。
 • 多行变量定义,为了追求代码排版美观,可将变量竖向对齐。
 • 代码行最大长度宜控制在必定个字符之内,能在当前屏幕内所有可见为宜。

7. switch语句

 • case关键字应和switch对齐。
 • case子语句若是有变量,应用{}包含起来。
 • 若是有并列的相似的简单case语句,可考虑将case代码块写为一行代码。
 • 简单的case之间可不用空行,复杂的case之间应考虑用空行分割开。
 • case字语句的大括号另起一行,不要和case写到一行。
 • 为全部switch语句提供default分支。
 • 若某个case不须要break必定要加注释声明。

8. 循环

 • 空循环可用 for( ;; ) 或者 while( 1 ) 或者 while( true )

9. 类

 • 类继承应采用每一个基类占据一行的方式。
 • 单继承可将基类放在类定义的同一行。若是用多行,则应用Tab缩进。
 • 多继承在基类比较多的状况下,应将基类分行,并采用Tab缩进对齐。
 • 重载基类虚函数,应在该组虚函数前写注释 // implement XXX
 • 友元声明放到类的末尾。

10. 宏

 • 不要用分号结束宏定义。
 • 函数宏的每一个参数都要括起来。
 • 不带参数的宏函数也要定义成函数形式。

11. goto

 • 尽可能不要用goto

5、类型


• 定义指针和引用时*和&紧跟类型。
• 尽可能避免使用浮点数,除非必须。
• 用typedef简化程序中的复杂语法。
• 避免定义无名称的类型。例如:typedef enum { EIdle, EActive } TState;
• 少用union,若是必定要用,则采用简单数据类型成员。
• 用enum取代(一组相关的)常量。
• 不要使用魔鬼数字。
• 尽可能用引用取代指针。
• 定义变量完成后当即初始化,勿等到使用时才进行。
• 若是有更优雅的解决方案,不要使用强制类型转换。

6、表达式


• 避免在表达式中用赋值语句。
• 避免对浮点类型作等于或不等于判断。
• 不能将枚举类型进行运算后再赋给枚举变量。
• 在循环过程当中不要修改循环计数器。

7、函数


1. 引用

 • 引用类型做为返回值:函数必须返回一个存在的对象。
 • 引用类型做为参数:调用者必须传递一个存在的对象。

2. 常量成员函数

 • 表示该函数只读取对象的内容,不会对对象进行修改。

3. 返回值

 • 除开void函数,构造函数,析构函数,其它函数必需要有返回值。
 • 当函数返回引用或指针时,用文字描述其有效期。

4. 内联函数

 • 内联函数应将函数体放到类体外。
 • 只有简单的函数才有必要设计为内联函数,复杂业务逻辑的函数不要这么作。
 • 虚函数不要设计为内联函数。

5. 函数参数

 • 只读取该参数的内容,不对其内容作修改,用常量引用。
 • 修改参数内容,或须要经过参数返回,用很是量应用。
 • 简单数据类型用传值方式。
 • 复杂数据类型用引用或指针方式。

8、类


1. 构造函数

 • 构造函数的初始化列表,应和类的顺序一致。
 • 初始化列表中的每一个项,应独占一行。
 • 避免出现用一个成员初始化另外一个成员。
 • 构造函数应初始化全部成员,尤为是指针。
 • 不要在构造函数和析构函数中抛出异常。

2. 纯虚函数

 • M类的虚函数应设计为纯虚函数。

3. 构造和析构函数

 • 若是类能够继承,则应将类析构函数设计为虚函数。
 • 若是类不容许继承,则应将类析构函数设计为非虚函数。
 • 若是类不能被复制,则应将拷贝构造函数和赋值运算符设计为私有的。
 • 若是为类设计了构造函数,则应有析构函数。

4. 成员变量

 • 尽可能避免使用mutableVolatile
 • 尽可能避免使用公有成员变量。

5. 成员函数

 • 努力使类的接口少而完备。
 • 尽可能使用常成员函数代替很是成员函数,const函数
 • 除非特别理由,毫不要从新定义(继承来的)非虚函数。(这样是覆盖,基类的某些属性无初始化)

6. 继承

 • 继承必须知足IS-A的关系,HAS-A应采用包含。
 • 虚函数不要采用默认参数。
 • 除非特别须要,应避免设计大而全的虚函数,虚函数功能要单一。
 • 除非特别须要,避免将基类强制转换成派生类。

7. 友元

 • 尽可能避免使用友元函数和友元类。

9、错误处理


• 申请内存用new操做符。
• 释放内存用delete操做符。
• newdeletenew[]delete[]成对使用。
• 申请内存完成以后,要检测指针是否申请成功,处理申请失败的状况。
• 谁申请谁释放。优先级:函数层面,类层面,模块层面。
• 释放内存完成后将指针赋空,避免出现野指针。
• 使用指针前进行判断合法性,应考虑到为空的状况的处理。
• 使用数组时,应先判断索引的有效性,处理无效的索引的状况。
• 代码不能出现编译警告。
• 使用错误传递的错误处理思想。
• 卫句风格:先处理全部可能发生错误的状况,再处理正常状况。
• 嵌套do-while(0)宏:目的是将一组语句变成一个语句,避免被其余if等中断。

相关文章
相关标签/搜索
本站公众号
   欢迎关注本站公众号,获取更多信息