1. 规则[ANSI]:函数的声明和实现必须符合ANSI语法
(1)函数的所有参数必须命名,并且必须在函数声明中说明各个参数的类型。
(2)禁止函数的参数为空。
例子:
正确写法 | 错误写法 |
F(int a, char *b); | f(int, char*); |
F(int a, char *b) { ...} | f(a, b) int a; char *b { ...} |
F(void); | f(); |
对于自动产生的代码可能违反该规则的例子:
void CTest2View::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: add cleanup after printing
}
你可采用两种方法:一是修改成符合格式的代码形式;
二是无法修改,则在函数前面添加注释:“此处为自动产生的代码,不方便修改。”。
2. 规则[ASSCAL]:不允许在函数调用中使用赋值运算符
不允许在函数调用中使用赋值运算符(=, +=, -=, *=, /=, %=, >>=, <<=, &=, |=, ^=,++,--)。目的是去除有关赋值顺序的不明确性。
例子:
正确写法 | 错误写法 |
n=f(b); b++; | n=f(b++); |
3. 规则[ASSCON]:不允许在控制指令的条件表达式中使用赋值运算符
不允许在if , while , for , switch控制指令中的条件表达式中使用赋值运算符(=, +=, -=, *=, /=, %=, >>=, <<=, &=, |=, ^=,++,--)。
例子:
正确写法 | 错误写法 |
X -= dx; if (x) { ... for (i=j=n; i > 0; i--, j--){ ... | if (x -= dx) { ...
for (i=j=n; i-- > 0; j--) {.. |
4. 规则[ASSEXP]:不允许在表达式内部嵌套赋值
在表达式内部,为了规避赋值顺序的不明确性,做如下规定:
(1)一个运算项只能被赋值一次。
(2)如果有多个赋值运算符,一个被赋值的运算项能且只能出现在它被赋值的位置。
例子:
正确写法 | 错误写法 |
| i = t[i++]; a=b=c+a; i=t[i]=15; |
5. 规则[BLOCKDECL]:声明必须在语句体的开始处
在语句体内,任何声明必须放在语句体的开始处。即:必须先声明后使用,而且必须集中在语句体的开始处声明。
例子:
正确写法 | 错误写法 |
{ int n; CAboutDlg aboutDlg; aboutDlg.DoModal(); n=3; } | { CAboutDlg aboutDlg; aboutDlg.DoModal(); int n; n=3; } |
6. 规则[BOOLEAN]:不允许使用缩写的布尔表达式
不允许使用缩写的布尔表达式。
例子:
正确写法 | 错误写法 |
AlwaysTrue = true; while (AlwaysTrue == true) { if (test == true) { if (ar.IsStoring()==true) { |
while (1) { if (test) { if (ar.IsStoring()){ |
7. 规则[BRKCONT]:break 和 continue指令禁止使用在控制语句中
break 和 continue指令禁止使用在控制语句(for, do ,while)中。然而,break指令被允许使用在switch语句体中。
像goto一样,这些指令会改变代码的结构。在循环中禁止使用它们将使代码比较容易理解。
例子:
正确写法 | 错误写法 |
int i,n; for (i=0;i<10;i++) { if (………) i=10; }
while (b==false) { if (n==10) { b=true; } } | int i,n; for (i=0;i<10;i++) { if (………) break; }
while (b==false) { if (n==10) { break; } } |
8. 规则[CLASSUSE]:禁止用户类串联调用
禁止通过串联(连续的)的调用方法,调用一个在用户类中不知道的类方法(隐式使用)。
下列表达式都是不允许的:u.v.a, u.v.f(),u.g().a, u.g().f(),(表达式中使用->操作符也是一样)。
例子:
正确写法 | 错误写法 |
| myWindow.itsButton.push(); Error->pos.line; |
9. 规则[CMCLASS]:每个类对应一个文件
一个代码文件中,每个函数都必须属于一个相同的类。
C函数被认为属于main类。
默认情况下,一个代码文件有一个如下的后缀:*.cc, *.cxx, *.cpp,*.C or *.c。
例子:
正确写法(解释) | 错误写法(解释) |
在一个.cpp文件中,只包含一个类的实现。
在一个.cpp文件中,只包含c函数的实现 | 在一个.cpp文件中,包含两个类的实现。
在一个.cpp文件中,既包含一个类的实现又包含c函数的实现。 |
10. 规则[CMDEF]:代码文件不能包含任何类的声明
代码文件不能包含任何类的声明。
C函数被认为属于main类。
默认情况下,一个代码文件有一个如下的后缀:*.cc, *.cxx, *.cpp,*.C or *.c。
例子:
正确写法(解释) | 错误写法(解释) |
在一个.cpp文件中,只包含一个类的实现。
| 在一个.cpp文件中,同时包含一个类的声明和实现。
|
11. 规则[CONDOP]:禁止使用三重条件操作符
三重条件操作符? ... : ...禁止使用。
12. 规则[CTRLBLOCK]:在控制语句段中必须使用{}
在控制语句段(if , for , while , do)中必须使用{}。消除结构范围的不清楚性以及使代码易读易改。
例子:
正确写法 | 错误写法 |
if (x == 0) { return; } else { while (x > min) { x--; } } | if (x == 0) return; else while (x > min) x--; |
13. 规则[DELARRAY]:当删除数组时必须使用[]
当删除数组时必须使用[]。确保恰当的内存被释放。
例子:
正确写法 | 错误写法 |
int *table = new int[7]; delete [] table; | int *table = new int[7]; delete table; delete [10] table;
|
14. 规则[CONSTRDEF]:每个类都必须明确包含它的默认构造函数
每个类都必须明确包含它的默认构造函数。确保作者已经考虑到初始化该类的方法。
例子:
正确写法 | 错误写法 |
class aClass { ... aClass(); ... }; |
|
15. 规则[DESTR]:每个类都必须明确包含它的析构函数
每个类都必须明确包含它的析构函数。确保作者已经考虑到销毁该类的实例的方法。
例子:
正确写法 | 错误写法 |
class aClass { ... ~aClass(aClass &object); ... }; |
|
16. 规则[DMACCESS]:类的接口只能是函数
类的接口必须是纯粹的函数:数据成员的定义被限制。
访问一个对象状态的好的方法是通过它的方法,而不是对象的数据成员。类的数据成员应当是private或者至少是protected。
例子:
正确写法 | 错误写法 |
class CTest2App : public CWinApp { public: int getactstate(); private: int m_act; | class CTest2App : public CWinApp { public: int m_actstate; |
17. 规则[EXPRCPLX]:表达式的复杂度必须小于一个给定的值24
表达式的复杂度必须小于一个给定的值。复杂度的计算与语法树以及它的节点个数有关。审核值复杂级为24。
例子:
(b+c*d) + (b*f(c)*d)
有 8个操作符和7个操作数.,语法树有16个节点,复杂度为16。
18. 规则[EXPRPARENTH]:每个二元的和三元的操作符必须放在()中
在表达式中,每个二元的和三元的操作符必须放在()中。
当右边的操作符与当前操作符同是+或者*则,禁止为右边的操作数使用();
同上,赋值操作符右边的操作数禁止使用();
禁止在表达式的第一级使用();
例子:
正确写法 | 错误写法 |
result = (fact / 100) + rem;
result = (fact * ind * 100) + rem + 10 + (coeff ** c); | result = fact / 100 + rem; result = ((fact / 100) + rem); result = ((fact * ( ind * 100)) + (rem + (10 + (coeff ** c)))); |
19. 规则[FNTYPE]:每个函数必须声明它的类型
每个函数必须声明它的类型。如果不返回任何值,它必须被声明为void类型。
20. 规则[FORINIT]:FOR循环的计数器必须在循环的初始化语句段中初始化
循环的计数器(在for循环中)必须在循环的初始化语句段中初始化。由循环头中的第三个元素来判断哪个是循环计数器。
例子:
正确写法 | 错误写法 |
for (i = 0; i < 10; i++) ... | for (i; i < 10; i++) ... for (j = 0; j < 10; i++) ... for (j = 10; i < j; i++) ... for (j = 1; i < funct(j); i+=j) ... |
21. 规则[FRNDCLASS]:友员类必须在类的开始处被声明
如果使用友员类,则友员类必须在类的开始处被声明(在类的成员被声明之前)。
22. 规则[FUNCPTR]:不使用函数指针
不使用函数指针。
正确写法 | 错误写法 |
| void (*func_1)(int); typedef void (*PFUNC)(int); PFUNC pfunc; |
23. 规则[FUNCRES]:特定的名字不能用来声明或者定义函数
特定的名字不能用来声明或者定义函数,也不能调用。默认情况下,不禁止任何函数名。主要用于禁止使用一些比较危险的系统函数。
目前我们不做任何限制。
24. 规则[GLOBINIT]:全局变量必须在定义的时候初始化
全局变量必须在定义的时候初始化。不是所有的编译器都给予相同的默认值。可以通过控制变量的值来避免一些不期望的动作。当声明全局变量的时候就赋初值可以确保在使用它们之前初始化。
25. 规则[GOTO]:goto语句不能使用
26. 规则[HEADERCOM]:文件必须有一个头注释
文件必须有一个头注释。头注释的格式如下:
///
// Name: program
// Author: Andrieu
// Date: 08/07/96
// Remarks: example of comments
///
27. 规则[HEADERCOM]:函数和类必须有注释
函数和类必须有注释。只要有一个/*,或者//开始的注释就可以了。
28. 规则[HMCLASS]:一个头文件一定不能包含多于一个类的定义
一个头文件一定不能包含多于一个类的定义。嵌套类可以接受。
29. 规则[HMDEF]:头文件不能包含代码段
头文件不能包含代码段(例如:数据和函数定义)
30. 规则[HMSTRUCT]:
头文件的主结构应当是这样的:
#ifndef <IDENT>
#define <IDENT>
...
#endif
或者
#if !defined (<IDENT>)
#define <IDENT>
...
#endif
这里,<IDENT>与头文件的名字保持一致。
31. 规则[IMPTYPE]:函数、参数、属性或者变量类型必须被明确定义
函数、参数、属性或者变量类型必须被明确定义。
正确写法 | 错误写法 |
void aFunction(int value); | aFunction(value); |
32. 规则[INCLTYPE]:只有一些类型的模块被允许包含在其他的模块中
只有一些类型的模块被允许包含在其他的模块中。默认情况下,头文件模块可以被包含在头文件模块和代码模块。
33. 规则[INLDEF]:内联函数必须在类中声明并且在其外实现
内联函数必须在类声明文件中(.h文件)中定义。
34. 规则[MACROPARENTH]:每个宏参数都应当包含在()中
在宏的定义中,每个宏参数都应当包含在()中。
正确写法 | 错误写法 |
#define GET_NAME(obj, ind ) (obj)->name[ ind ]
| #define GET_NAME(obj, ind ) obj->name[ ind ]
|
35. 规则[MCONST]:宏常量的使用限制
对于字符串常量使用全局或者静态变量,其余的常量可以使用宏定义
正确写法 | 错误写法 |
const char *string = "Hello world!/n";
#define value 3
| #define string "Hello world!/n"
|
36. 规则[MFUNC]:使用内联函数替代宏函数
使用内联函数替代宏函数。相对于宏函数,内联函数可以检查它的参数类型。
正确写法 | 错误写法 |
inline char *GetName(aClass &object) { return(object.name); }
inline min (int i, int j) { return (i<j)?i:j; }
| #define GetName(s) ((s)->name)
#define MIN(i,j) ((i)<(j)) ? (i) : (j)
|
37. 规则[MNAME]:文件名必须来自于在文件中声明或者定义的类的名字
文件名必须来自于在文件中声明或者定义的类的名字。
38. 规则[MULTIASS]:一个语句中赋值操作符不能使用多于一次
每个语句中,赋值操作符(=, +=, -=, *=, /=, %=, >>=, <<=, &=, |=,^=, ++, --)不能使用多于一次。消除运算顺序的不清楚。
正确写法 | 错误写法 |
c = 5; b = c; b++; a = (b * c) + 5; | b = c = 5; a = (b++ * c) + 5; |
39. 规则[NOPREPROC]:未指明的指令不允许使用
除了在参数列表中配置的那些指令,其它未指明的指令不允许使用。
只有 #line 和 # alone不可以使用。
“define”: #define may be used
“include”: #include may be used
“if”: #if, #ifdef and #ifndef may be used
“pragma”: #pragma may be used
“undef”: #undef may be used
“line”: #line may be used
“error”: #error may be used
“none”: # none may be used
40. 规则[PARSE]:定义一些不能被解析的模块
定义哪些代码可以被分析,哪些代码拒绝c++RuleChecker的分析。
41. 规则[PMFRTN]:成员函数一定不能返回成员数据的指针或者成员数据的非常量形式的引用
成员函数一定不能返回成员数据的指针或者成员数据的非常量形式的引用。帮助确保遵守数据封装。
42. 规则[PTACCESS]:指针使用ptr->field格式的语法
使用ptr->field格式的语法,不能使用(*ptr).field格式的语法。
43. 规则[PTRINIT]:指针必须在声明的时候初始化
每一个被明确声明为指针(使用”*”)的自变量,必须在声明的时候初始化。确保指针变量在使用之前就被恰当的初始化。
正确写法 | 错误写法 |
int* y=&x; ...
| int *y ; *y=&x ;
|
44. 规则[RTNLOCPTR]:函数一定不能返回一个非静态的本地变量的指针
函数一定不能返回一个非静态的本地变量的指针。避免使用一个不在生命周期内的变量的指针。
45. 规则[SGDECL]:禁止在一个声明中同时声明多个变量
变量的声明必须是如下的格式:
type variable_name;。
禁止在一个声明中同时声明多个变量。
46. 规则[SGLRETURN]:一个函数只能有一个return语句
47. 规则[SLSTAT]:每一行不能多于一个语句
每一行不能多于一个语句。一个语句跟随一个{,或者一个{跟随一个语句在一行之内都是允许的,但是如果一行之内语句跟随{再跟随语句就是不允许的。
正确写法 | 错误写法 |
x = x0; y = y0; while (IsOk(x)) { x++; } | x = x0; y = y0; while (IsOk(x)) {x++;} while (IsOk(x)) {x++; } |
48. 规则[SWDEF]:在switch语句体中,强制使用default
在switch语句体中,为了覆盖没有case的其它情况强制使用default
49. 规则[SWEND]:在switch语句体中,每一个case都必须使用break,continue,goto,return,或者exit结束。
50. 规则[TYPEINHER]:类继承的类型(public, protected, private)必须指明
例子:(正确)
class inherclass : public Base1, private Base2
{...
51. 规则[VARARG]:禁止函数使用可变量参数
禁止函数使用可变量参数,例如:va_list类型和… 。
52. 规则[VARINIT]:所有的变量在使用之前必须初始化
53. 规则[VARSTRUCT]:变量不能使用struct或者union直接声明
变量不能使用struct或者union直接声明,必须定义一个中间类型。
正确写法 | 错误写法 |
typedef struct { ... } typeName; typeName varName;
struct structName; typedef struct structName { ... struct structName *ptr; } typeName; typeName varName;
| struct { ... } varName;
struct structName { ... }; struct structName varName; |
54. 规则[VOIDPTR]:空类型的指针(void *)禁止使用
55. 规则[ASSIGNTHIS]:确保可以自赋值
在赋值操作符的定义中,必须检查参数与this(或者与*this)是否相等,如果相等,必须返回*this。
56. 规则[CATCHREF]:在catch子句中,使用引用来传递参数
例子:
try {
someFunction();
}
catch (exception& ex) { // 这里,我们通过引用捕获异常
cerr << ex.what();
}
57. 规则[CONVNEWDEL]:重写new和delete操作符时,必须保持和系统缺省的 new、delete的行为一致
new必须遵守:
返回值必须是void *
第一个参数类型必须是site_t
delete必须遵守:
返回值必须是void
第一个参数必须是void *
第二个参数类型必须是site_t
58. 规则[DATAPTR]:类成员不允许是对象指针
59. 规则[DEFIFNEW]:如果在一个类中声明了操作符new就要在该类中同时声明操作符delete
60. 规则[INLINEVIRT]:虚函数不能被声明为inline
61. 规则[MULTINHER]:如果使用多继承,被继承的类(父类)必须是抽象的,也就是说,它们必须要有至少一个纯粹的虚函数。
62. 规则[NONLEAFABS]:非叶节点类一定要是抽象类
63. 规则[NORMALNEW]:如果在一个类中定义了一次或者多次new操作符,则这些定义中,必须有一个遵守new的一般模式
new的一般模式:
第一个参数的类型必须是size_t。
如果还有其他参数,每个参数都必须有一个默认值。
64. 规则[OVERLOAD]:"&&", "||" and ","三个操作符不允许重载
65. 规则[PREPOST]:++和--操作符声明方式
++和--操作符必须按照如下方式声明:
class Example {
public:
Example& operator++(); // 前缀++
const Example operator++(int); // 后缀++
Example& operator--(); // 前缀 --
const Example operator--(int); // 后缀 --
}
66. 规则[REFCLASS]:每个类类型的参数都必须使用传引用的方法
67. 规则[RETURNTHIS]:在一个赋值操作符的定义中,返回值必须是*this
68. 规则[VIRTDESTR]:基类(父类)的析构函数应当声明为virtual
69. 规则[MACROCHARSET]:需要在宏函数和宏常量的定义书写中禁止使用的字符
默认情况下,不禁止任何字符。
70. 规则[IDENTRES]:一些标识符被禁止用于声明
默认情况下,不禁止任何字符。
71. 规则[TYPERES]:禁止某些类型用于变量和函数的声明
默认情况下,不禁止任何类型。