C/C++规范

本文摘自 高质量 C++/C 编程指南  作者 林锐

优先级 :非与或       算关逻

C/C++是大小写敏感的。故程序中不要出现仅靠大小写区分的相似的标识符。

程序中不要出现标识符完全相同的局部变量和全局变量,尽管两者的作用域不同而不会发生语法错误,但会使人误解。

 头文件格式:如果一个软件的头文件数目比较多(如超过十个),通常应将头文件和定义文件分别保存于不同的目录,以便于维护。 

包含守卫:

#ifndef  GRAPHICS_H //  防止 graphics.h 被重复引用 

#define  GRAPHICS_H  

#include <math.h>              //  引用标准库的头文件 ,编译器将从标准库目录开始搜索

#include “myheader.h”    //  引用非标准库的头文件 ,编译器将从用户的工作目录开始搜索

void Function1(⋯);               // 全局函数  声明  不提倡使用  全局变量  ,尽量不要在头文件中出现象 extern int value 这类声明。

class Box{ };                         // 类结构声明  

#endif 

在 C++ 语法中,类的成员函数可以在声明的同时被定义,并且可自动成为内联函数。这虽然会带来书写上的方便,但却造成了风格不一致,弊大于利。

建议将成员函数的定义与声明分开,不论该函数体有多么小。

  

 定义文件的结构:

#include “graphics.h”      //  引用头文件                                            

void Function1(⋯)   { }        //全局函数的实现体

 void Box::Draw(⋯) { }         // 类成员函数的实现体 

 

if 、for、while 、do 等语句自占一行,执行语句不得紧跟其后。不论执行语句有多少都要加{} 。这样可以防止书写失误。

 

应当将修饰符 *  和  &  紧靠变量名 ,因为修饰符优先和变量名结合  在 int *x, y; 中x是指针,y是整型变量。 

 

将 public 类型的函数写在前面,而将 private 类型的数据写在后面,类的设计“以行为为中心”,方便阅读。

 

简单的 Windows 应用程序命名规则:

1、类名和函数名用大写字母开头的单词组合而成。

2、变量和参数用小写字母开头的单词组合而成。 

3、常量全用大写的字母,用下划线分割单词。 

4、静态变量加前缀 s_(表示 static)

5、如果不得已需要全局变量,则使全局变量加前缀 g_(表示 global )。

6、类的数据成员加前缀 m_(表示 member),这样可以避免数据成员与成员函数的参数同名。

7、为了防止某一软件库中的一些标识符和其它软件库中的冲突,可以为各种标识符加上能反映软件性质的前缀。

      例如三维图形标准 OpenGL 的所有库函数均以 gl 开头,所有常量(或宏定义)均以 GL 开头。 

 

不可将布尔变量直接与 TRUE、FALSE 或者 1、0 进行比较。 根据布尔类型的语义,零值为“假”(记为 FALSE),任何非零值都是“真”(true)。

TRUE 的值究竟是什么并没有统一的标准。/*那么设置TRUE和FALSE有什么用  ?*/

if (flag)  // 表示 flag 为真 

if (!flag) // 表示 flag 为假 

其它的用法都属于不良风格,例如:
if (flag == TRUE)                                           if (flag == FALSE) 
if (flag == 0)                                                  if (flag == 1 )  

应当将整型变量用“==”或“!=”直接与 0 比较。 不可模仿布尔变量的风格而写成 if (value)    会让人误解 value 是布尔变量 。

 

不可将浮点变量用“==”或“!=”与任何数字比较。 无论是 float 还是 double 类型的变量,都有精度限制。所以一定要避

免将浮点变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。    http://www.cnblogs.com/jiahu-Blog/p/4502990.html

 

应当将指针变量用“==”或“!=”与 NULL 比较。 不要写成 if (p == 0)   容易让人误解 p 是整型变量 

有时候我们可能会看到 if (NULL == p) 这样古怪的格式。不是程序写错了,是程序员为了防止将 if (p == NULL) 误写成 if (p = NULL),而有意把 p 和 NULL 颠倒。

编译器认为 if (p = NULL) 是合法的,但是会指出 if (NULL = p)是错误的,因为 NULL不能被赋值。

 

在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少 CPU 跨切循环层的次数。

 

如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面。使得编译器能对循环进行优化处理,增加效率。

  for (i=0; i<N; i++)  

{   

if (condition) 
DoSomething(); 
else 
DoOtherthing(); 

}

效率低但程序简洁 

if (condition) 

      for (i=0; i<N; i++) 
      DoSomething(); 

else 

     for (i=0; i<N; i++) 
     DoOtherthing(); 
}

效率高但程序不简洁 

 

在switch语句中不要忘记最后那个 default 分支。即使程序真的不需要 default 处理,也应该保留语句 default : break; 这样做并非多此一举,

而是为了防止别人误以为你忘了 default 处理。

 

在 C++ 程序中只使用 const 常量而不使用宏常量,即 const 常量完全取代宏常量。

const 数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的,因为类可以创建多个对象,不同的对象其 const 数
据成员的值可以不同。 不能在类声明中初始化 const 数据成员,在构造函数的初始化表 中初始化常量。

在整个类中使用常量的方法可以是static const  *   或使用    枚举    enum { SIZE1 = 100, SIZE2 = 200}; // 枚举常量 

 

函数参数的规则 :

1、如果参数是指针,且仅作输入用,则应在类型前加 const,以防止该指针在函数体内被意外修改。

        例如:void StringCopy(char *strDestination,const char *strSource); 

2、如果输入参数以值传递的方式传递对象,则宜改用“const &”方式来传递,这样可以省去临时对象的构造和析构过程,从而提高效率。

 

返回值的规则 :

1、不要省略返回值的类型,即使没有返回值也要在函数前加上void 。(除构造与析构函数,构造和析构函数不允许有返回值,包括void。因为构造函数与析构函数会被编译器自动调用,那么如果允许构造函数返回值有些时候会产生歧义

  C 语言中,凡不加类型说明的函数,一律自动按整型处理。这样做不会有什么好处,却容易被误解为 void 类型。

2、函数名字与返回值类型在语义上不可冲突。 违反这条规则的典型代表是 C 标准库函数 getchar。 例如: 

char c;
c = getchar();
if (c == EOF)      

getchar 返回的不是 char 类型,而是 int 类型。其原型如下  int  getchar(void);  这样会误导使用者,上面的代码中EOF是整型,无法保存在c中,会造成错误。

3、不要将正常值和错误标志混在一起返回。正常值用输出参数获得,而错误标志用 return 语句返回。

      函数 getchar 可以改写成  BOOL GetChar(char *c);         //用c保存结果,而返回函数是否执行顺利的标志。

4、有时候函数原本不需要返回值,但为了增加灵活性如支持链式表达,可以附加返回值。

5、如果函数的返回值是一个对象,有些场合用“引用传递”替换“值传递”可以提高效率。而有些场合只能用“值传递”而不能用“引用传递”,否则会出错。

6、return 语句不可返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁。

7、如果函数返回值是一个对象,要考虑 return 语句的效率。例如 

                  return String(s1 + s2);  //编译器直接把临时对象创建并初始化在外部存储单元中,省去了拷贝和析构的花费,提高了效率。

这是临时对象的语法,表示“创建一个临时对象并返回它”。不要以为它与“先创建一个局部对象 temp 并返回它的结果”是等价的,如
String temp(s1 + s2);
return temp;
实质不然,上述代码将发生三件事。首先,temp 对象被创建,同时完成初始化;然后拷贝构造函数把 temp 拷贝到保存返回值的外部存储单元中;

最后,temp 在函数结束时被销毁(调用析构函数)。

关于函数的其它建议 :

1、函数的功能要单一,不要设计多用途的函数。 

2、尽量避免函数带有“记忆”功能。相同的输入应当产生相同的输出。 带有“记忆”功能的函数,其行为可能是不可预测的,因为它的行为可能取决于某种“记忆状           态”。这样的函数既不易理解又不利于测试和维护。在 C/C++ 语言中,函数的 static 局部变量是函数的“记忆”存储器。建议尽量少用static 局部变量,除非必          需。 

3、不仅要检查输入参数的有效性,还要检查通过其它途径进入函数体内的变量的有效性,例如全局变量、文件句柄等。
4、用于出错处理的返回值一定要清楚,让使用者不容易忽视或误解错误情况。

 

使用断言:

1、断言 assert 是仅在 Debug 版本起作用的宏,它用于检查“不应该”发生的情况。assert 不是一个仓促拼凑起来的宏。为了不在程序的 Debug 版本和 Release 版本      引起差别,assert 不应该产生任何副作用。所以 assert 不是函数,而是宏。

2、在函数的入口处,使用断言检查参数的有效性(合法性)。 

 

第七章见内存管理一章。

原文地址:https://www.cnblogs.com/jiahu-Blog/p/4502314.html