现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

软件开发代码规范(VC版)

2012-04-15 16:20 工业·编程 ⁄ 共 10954字 ⁄ 字号 暂无评论

    在团队协作开发的情况下,编程时应该强调的一个重要方面是程序的易读性,在保证软件的速度等性能指标能满足用户需求的情况下,能让其他程序员容易读懂你的程序。一套鲜明的编程风格,可以让协作者、后继者和自己一目了然,在很短的时间内看清程序的结构,理解设计的思路。大大的提高代码的可读性、可重用性、程序健壮性、可移植性和可维护性。

    制定本编程规范的目的是为了提高软件开发效率及所开发的软件的可维护性,提高软件的质量。本规范基于VC开发语言,由程序风格、命名规则、注释规范、程序健壮性、可移植性、错误处理以及软件的模块化规范等部分组成。

    本规范主要适用于WINDOWS平台相关代码编写,对于跨操作系统的标准C/C++代码规范,请参见《软件开发代码规范(C/C++版)》。

》》》新手必读:关于软件开发的一些答疑

一、程序风格

    1、严格采用阶梯层次组织程序代码:

各层次缩进的分格采用VC的缺省风格,即每层次缩进为4格,括号位于下一行。要求相匹配的大括号在同一列,对继行则要求再缩进4格。例如:

void main()

{

......

long lI; // 循环变量

long lSum; // 用来记录和

float fAvg; // 用来求平均值

......

// 对数进行累加。

for( lI=0;lI<10;lI++)

{

lSum=lSum+lI;

......
}

// 求平均值。

fAvg=lSum/10.0;

......

}

    2、提示信息字符串的位置

在程序中需要给出的提示字符串,为了支持多种语言的开发,除了一些给调试用的临时信息外,其他所有的提示信息必须定义在资源中,以利于翻译成多种语言,以便多版本共用一套代码。

    3、对变量的定义,尽量位于函数的开始位置,并进行初始化。

二、命名规则

     1、变量名的命名规则

①、变量的命名规则要求用“匈牙利法则”,尽量做到“望文生义”。

即开头字母用变量的类型,其余部分用变量的英文意思或其英文意思的缩写,尽量避免用中文的拼音,要求单词的第一个字母应大写。即:

变量名 = 变量类型 + 变量的英文意思(或缩写)

对非通用的变量,在定义时加入注释说明,变量定义尽量可能放在函数的开始处。

见下表:

bool(BOOL) 用b开头 bIsParent

byte(BYTE) 用by开头 byFlag

short(int) 用n开头 nStepCount

long(LONG) 用l开头 lSum

char(CHAR) 用c开头 cCount

float(FLOAT) 用f开头 fAvg

double(DOUBLE) 用d开头 dDeta

void(VOID) 用v开头 vVariant

unsigned int(WORD) 用w开头 wCount

unsigned long(DWORD) 用dw开头 dwBroad

HANDLE(HINSTANCE) 用h开头 hHandle

DWORD 用dw开头 dwWord

LPCSTR(LPCTSTR) 用str开头 strString

用0结尾的字符串 用sz开头 szFileName

对未给出的变量类型要求提出并给出命名建议给代码质量管理组。

②、指针变量命名的需标清。

对一重指针变量的基本原则为:

p + 变量类型前缀 + 命名

如一个float* 型应该表示为pfStat

对多重指针变量的基本规则为:

二重指针: pp + 变量类型前缀 + 命名

三重指针: ppp + 变量类型前缀 + 命名

......

②、全局变量用g_开头。

如一个全局的长型变量定义为g_lFailCount,即:

变量名 = g_ + 变量类型 + 变量的英文意思(或缩写)

③、静态变量用s_开头。

如一个静态的指针变量定义为s_plPerv_Inst,即:

变量名 = s_ + 变量类型+变量的英文意思(或缩写)

④、成员变量用m_开头。

如一个长型成员变量定义为m_lCount,即:

变量名 = m_ + 变量类型 + 变量的英文意思(或缩写)

    2、函数的命名规范:

    函数的命名应该尽量用英文表达出函数完成的功能。遵循动宾结构的命名法则,函数名中动词在前,函数名的长度不得少于6个字母。例如:long GetDeviceCount(……);

    3、函数参数规范:

①、参数的命名参照变量命名规范。

②、为了提高程序的运行效率,减少参数占用的堆栈,传递大结构的参数,一律采用指针或引用方式传递。

③、为了便于其他程序员识别某个指针参数是入口参数还是出口参数,同时便于编译器检查错误,应该在入口参数前加入const标志。如:

……CopyString(const char * c_szSource, char * szDest)

    4、引出函数规范:

    对于从动态库引出作为二次开发函数公开的函数,为了能与其他函数以及Windows的函数区分,采用类别前缀+基本命名规则的方法命名。例如:在对动态库中引出的一个图象编辑的函数定义为 imgFunctionname(其中img为image缩写)。

现给出三种库的命名前缀:

①、对通用函数库,采用cm为前缀。

②、对三维函数库,采用vr为前缀。

③、对图象函数库,采用img为前缀。

对宏定义,结果代码用同样的前缀。

    5、文件名(包括动态库、组件、控件、工程文件等)的命名规范:

    文件名的命名要求表达出文件的内容,要求文件名的长度不得少于5个字母,严禁使用象file1,myfile之类的文件名。

三、注释规范

    1、函数头的注释

    对于函数,应该从“功能”,“参数”,“返回值”、“主要思路”、“调用方法”、“创建日期”、“维护记录”六个方面,例如函数 double GetDistance(const POINT3d A, const POINT3d B) 的函数头的注释格式可以如下:

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////GeoView_Tech_Mark_Begin

//==================================================================//

//GeoView_Doc_Mark_Begin //

// 函 数 名: GetDistance

// 功 能: 求两点间的距离

// 参 数: const POINT3d A, const POINT3d B

// (入口): A: 点A

// B: 点B

// (出口): 无

// 返 回: double: 点A与点B之间的距离(欧氏距离)

// 调用方法: double dDist = 0.0;

// POINT3d A = {0.0, 0.0, 0.0}, B = {1.0, 2.0, 3.0};

// dDist = GetDistance(A, B);

//GeoView_Doc_Mark_End //

// 主要思路: 使用欧氏距离的计算公式求出两点的距离

// 参 阅: 欧几里德德《几何原理》

// 创建日期: 2004-3-8: 张三

// 修改日期: 2004-3-8: 李四, 修改了计算公式, 原来的有错误

//==================================================================//

double GetDistance(const POINT3d A, const POINT3d B)

////GeoView_Tech_Mark_End

{

…… // 函数实现部分

}

其中GeoView_Tech_Mark_Begin到GeoView_Tech_Mark_End部分用于自动提技术开发文档的标识,GeoView_Doc_Mark_Begin到GeoView_Doc_Mark_End部分用于自动提取二次开发文档的标识。

①、对于某些函数,其部分参数为传入值,而部分参数为传出值,所以对参数要详细说明该参数是入口参数,还是出口参数,对于某些意义不明确的参数还要做详细说明(例如:以角度作为参数时,要说明该角度参数是以弧度(PI),还是以度为单位),对既是入口又是出口的变量应该在入口和出口处同时标明。等等。

②、函数的注释应该放置在函数的头文件中,在实现文件中的该函数的实现部分应该同时放置该注释。

③、在注释中应该详细说明函数的主要实现思路、特别要注明自己的一些想法,如果有必要则应该写明对想法产生的来由。对一些模仿的函数应该注释上函数的出处。

④、在注释中详细注明函数的适当调用方法,对于返回值的处理方法等。在注释中要强调调用时的危险方面,可能出错的地方。

⑤、对修改日期的注释要求记录整个修改过程和修改人。

如果算法比较复杂,或算法中的变量定义与位置有关,则要求对变量的定义进行图解。对难以理解的算法能图解尽量图解。

    2、变量的注释:

对于变量的注释紧跟在变量的后面说明变量的作用。原则上对于每个变量应该注释,但对于意义非常明显的变量,如:i,j等循环变量可以不注释。例如:

long lLineCount; // 线的根数

    3、文件的注释:

文件应该在文件开头加入版权声明以及头注释,例如:

// GeoHazard3DDoc.cpp: implementation of Class CGeoHazard3Ddoc

// 版权所有(C) 2005 武汉地大坤迪科技有限公司。保留所有权利。

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//工 程: GeoHazard (文件所在的项目名) //

//作 者: 王麻子 //

//修改者: 李四 //

//描 述: 文档类的实现文件 //

//创建日期: 2004-3-11: 王麻子 //

//修改日期: 2005-7-18: 李四, 增加了函数…. //

//参考文献: //

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

为了头文件被重复包含要求对头文件进行定义如下:

#ifndef __FILENAME_H__

#define __FILENAME_H__

其中FILENAME为头文件的名字。

    4、其他注释:

在函数内我们不需要注释每一行语句。但必须在各功能模块的每一主要部分之前添加块注释,注释每一组语句,在循环、流程的各分支等,尽可能多加以注释。

其中的循环、条件、选择等位置必须注释

对于前后顺序不能颠倒的情况,建议在注释中增加序号。

例如:

......

// 1、......注释

for (......)

{

}

if(......)

{// ......注释

}

else

{// ......注释

}

// ......注释

switch(......)

{

case: ......// ......注释

......

case: ......// ......注释

......

default: // ......注释

......

}

在其他顺序执行的程序中,每隔3—5行语句,必须加一个注释,注明这一段语句所组成的小模块的作用。对于自己的一些比较独特的思想要求在注释中标明。

四、程序健壮性

    1、函数的返回值规范:

    对于函数的返回位置,尽量保持单一性,即一个函数尽量做到只有一个返回位置。(单入口单出口)。要求大家统一函数的返回值,所有的函数的返回值都将以编码的方式返回。

例如编码定义如下:

#define CM_POINT_IS_NULL CMMAKEHR(0X200)

建议函数实现如下:

long 函数名(参数,……)

{

long lResult; //保持错误号

lResult=CM_OK;

// 如果参数有错误则返回错误号

if(参数==NULL)

{

lResult=CM_POINT_IS_NULL;

goto END;

}

……

END:

return lResult;

}

    2、关于goto的应用:

我们要求尽量少用goto语句。对一定要用的地方要求只能向后转移。

    3、资源变量的处理(资源变量是指消耗系统资源的变量):

对资源变量一定赋初值。分配的资源在用完后必须马上释放,并重新赋值。例:

long* plAllocMem; //定义一个分配内存的变量。

plAllocMem=(long*)calloc(40, sizeof( long )); //分配一段内存。

//处理分配内存错误

if(plAllocMem==NULL)

{

lResult=CM_MEM_ALLOC_FAILED;

goto END;

}

……

使用内存

……

// 释放资源变量,并重新赋值。

if(pAllocMem!=NULL)

{

free(plAllocMem);

pAllocMem=NULL;

}

    4、对复杂的条件判断,为了程序的可读性,应该尽量使用括号。

    例:if(((szFileName!=NULL)&&(lCount>=0)))||(bIsReaded==TRUE))

五、可移植性

    1、高质量的代码要求能够跨平台,所以我们的代码应该考虑到对不同的平台的支持,特别是对windows98和windowsnt的支持。

    2、由于C语言的移植性比较好,所以对算法函数要求用C代码,不能用C++代码。

    3、对不同的硬件与软件的函数要做不同的处理。

六、错误处理

    1、错误报告处理。

编程中要求考虑函数的各种执行情况,尽可能处理所有的流程情况。将函数分为两类:

一类为与屏幕的显示无关,(不与用户交换信息的函数)

一类为与屏幕的显示相关。(与用户交换信息的函数)

对于与屏幕显示无关的函数,函数通过返回值来报告错误。

对于与屏幕显示有关的函数,函数要负责向用户发出警告,并进行错误处理。

错误处理代码一般单独建立通用处理函数。如下:

void cmDeal_With_Error(long ErrCode)

{

switch(ErrCode)

{

case 1://注释

......

case 2://注释

......

default://注释

......

}

}

    2、尽早发现程序中的错误:

①、重视编译器中的警告信息。

对于编译器产生的警告信息,我们应该引起足够的重视,实际上许多警告信息指示了程序中潜在的错误危险。所以我们要认真检查每一个警告信息,查看是否有某种隐患。尽量消除警告信息。

②、利用断言来检查错误

对于程序中的某种假设,或防止某些参数的非法值,利用断言来帮助查错是一种好办法。

例如下面的函数:

long cmMemCpy(void * pvToMem, void* pvFromMem, size_t wSize)

{

……

if(pvToMem==NULL||pvFromMem==NULL)

{

lResult=CM _POINT_IS_NULL;

goto: END;

}

while(wSize-- >0)

{

*pvToMem++=pvFromMem++;

}

END:

return lResult;

}

采用判断可以检查传入的指针错误,但是这样的判断是程序最终的编译代码变大,同时降低了最终发布的程序的执行效率。由于传入空指针明显是调用这函数的程序的错误,而不是这个函数的错误,我们可以考虑采用断言来代替指针检查,即用

ASSERT( pvToMem!=NULL&&pvFromMem!=NULL)

代替

if(pvToMem==NULL||pvFromMem==NULL)

{

lResult=CM_POINT_IS_NULL;

goto: END;

}

这样只会在debug版中才会产生检查代码,而在正式发布版中不会带有这些代码。并且可以方便我们在程序调试中和测试时发现错误,同时又不影响程序的效率。

在下面的一些情况中必须加断言:

a、 数的参数,特别是指针参数必须利用断言来进行确认。

b、 利用断言检查程序中的各种假设的正确性。

c、 在程序设计中不要轻易认为某种情况不可能发生,对你认为不可能发生的情况必须用断言来证实。

为了使程序中的断言发挥作用,所有用于在开发内部进行测试或调试的动态库、执行程序、组件必须采用debug版。

说明:

在程序效率要求较高、或者调用比较频繁的函数,对入口参数的错误检查,使用断言方式,其优点如上所叙,但其健壮性不强,所以在其他情况下,仍要求使用传统的检查方式,以增强程序的健壮性,当然,为了调试方便,可同时使用断言方式。

③、严格的测试:

对每一段代码都要求进行严格的测试,特别对一些功能函数要对其各种临界点(比如零值、无穷大的值等)进行测试。尽量做到每一段代码零错误。

七、模块化规范

    为了提高软件的重用性,减少重复开发的工作量。同时也为了提高程序的可读性,方便程序的维护,必须加强软件的模块化工作。模块化应该遵循以下几个基本规范:

    1、函数应该作到精而小,函数的代码应该控制在一个适度的规模,每个函数的代码一般不能超过150行,如果超过这个规模,应该进行模块化的工作。对于一些特殊的函数确实要超过150行,应该提交出来讨论,通过后,要求编写者更加详细的对函数注释,并写明函数超行的原因,以及设计思想等。

    2、某一功能,如果重复实现三遍以上,既应该考虑模块化,将其写成通用函数。并向开发人员发布。并要求将接口文档和实现的功能备案。

    3、 每一个开发人员要尽可能的利用其他人的现成的模块,减少重复开发。

    4、对函数进行模块化时,要考虑函数的层次关系,特别是在增加新的功能模块时,对原来的函数代码要进行认真的调整,做到相同功能的不同函数没有重复代码,此要求的目的在于便于代码维护。举例如下:

    现有如下函数:

//从szFileName文件中取 ......

long ...... cmGetSomething(const char * c_szFileName,......)

{

CFile * pFile;//用来保存打开文件的地址

pFile=new CFile(c_szFileName,CFile::modeRead);//用创建一个只读文件

if(pFile==NULL)

{

lResult=CM_POINT_IS_NULL;

goto END;

}

//从文件中读取......

......

//关闭文件

delete pFile;

END:

return lResult;

}

若现在需要增加如下接口的新函数:

long ...... cmReadSomething(CFile * pFile)

{

if(pFile==NULL)

{

lResult=CM_POINT_IS_NULL;

goto END;

}

//从文件中读取......

......

END:

return lResult;

}

则要求如下:

将 long ......cmGetSomething(const char * c_szFileName,......)改为

long ...... cmGetSomething (const char * c_szFileName,......)

{

CFile * pFile; //用来保存打开文件的地址

long lResult=CM_OK;//错误返回码

//打开文件

pFile=new CFile(c_szFileName,CFile::modeRead);

if(pFile==NULL)

{

lResult=CM_POINT_IS_NULL;

goto END;

}

//从文件中读取......

lResult=cmReadSomething(pFile,......);

IF_ERROR_GOTO_END

//关闭文件

delete pFile;

END:

return lResult;

}

    模块化的一些注意事项:

① 、设计好模块接口,用面向对象的观点看,包括:函数接口和变量接口。

② 、定义好接口以后不要轻易改动,并在模块开头(文件的开头或函数的开头)加以说明,所以在定义接口时,一定要反复琢磨,保持风格一致。

③ 、注意全局变量也是一种接口,如果不是确实必要,应该尽量少用全局变量。

④ 、在函数接口中,尽量使函数的接口容易理解和使用,其中每个输入输出参数都只代表一种类型数据,不要把错误值和其他专用值混在函数的其他输入输出参数中。

⑤ 、争取编写出永远成功的函数,使调用者不必进行相应的错误处理。

八、类(class)的规范化

    1、类成员声明按以下顺序排列

成员函数

public:

构造函数

析构函数

protected:

private:

成员变量

public:

protected:

private:

其中,成员变量(命名规则按上述规定,不同之处在前面加“m_”),成员函数的住释以及自动提取标识的位置与前面的规定相同。

  2、应尽量避免在一个实现文件中实现多个类,如果确有必要,则每一个类的实现成员应放在一起;

  3、禁止在类定义里面实现成员函数,只允许定义.哪怕只有函数实现只有一句话也要放到外面实现,以避免在自动提取开发手册出现问题.

  4、 对于类的成员函数的说明按照MSDN的方式,比如:重载的成员函数放在一起进行参数说明,成员函数的命名规则也参照MSDN等等,加标识可以按如下,可选参数加[]:.

   5、类定义的缩进规则按VC缺省的缩进规则.

  6、在一个类或结构的前面应有一段对类的逻辑流程、算法的简短说明,格式如下例所示:

///////////////////////////////////////////////////////////////////////////////////

//// GeoView_Class_Mark_Begin

// 类 名: CAboutDlg

// 基 类: CDialog

// 作 者: 张三

// 描 述: "关于..."对话框

// 创建日期: 2004-3-11

// 修 改: 2005-7-18: 李四, 增加了函数…

// 参考文献: MSDN

///////////////////////////////////////////////////////////////////////

class CAboutDlg : public CDialog

{

public:

CAboutDlg(); // 构造函数

// Dialog Data

//{{AFX_DATA(CAboutDlg)

enum { IDD = IDD_ABOUTBOX };

//}}AFX_DATA

// ClassWizard generated virtual function overrides

//{{AFX_VIRTUAL(CAboutDlg)

protected:

virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support

//}}AFX_VIRTUAL

// Implementation

protected:

//{{AFX_MSG(CAboutDlg)

virtual BOOL OnInitDialog(); // 初始化对话框

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

};

//// GeoView_Class_Mark_END

///////////////////////////////////////////////////////////////////////////////////

九、结构、枚举、常量或宏的规范说明

    1、结构规定:

//////////////////////////////////////////////////////////////////////////

////GeoView_Struct_Mark_Begin

// 结构描述: 点的结构

// 作 者: 李四

// 创建日期: 2004-3-8

// 修 改: 2005-7-18: 张三,增加了…

///////////////////////////////////////////////////////////////////////

typedef struct

{

double x, y, z; // 点的X,Y,Z坐标值

} XYZ; // 点的结构定义

////GeoView_Struct_Mark_End

//////////////////////////////////////////////////////////////////////////

    2、枚举规定:

/////////////////////////////////////////////////////////////////////

//// GeoView_Enum_Mark_Begin

// 枚举描述: 枚举CImageEx类支持的图象类型。

// 作 者:田宜平

// 创建日期: 2002-10-19。

// 修 改: 2005-7-18:何珍文, 说明对文件的修改内容、修改原因以及修改日期。

enum

{ // CImageEx类支持的图象类型

#if CXIMAGE_SUPPORT_BMP

CXIMAGE_FORMAT_BMP, // BMP图像格式

#endif

#if CXIMAGE_SUPPORT_GIF

CXIMAGE_FORMAT_GIF, // GIF图像格式

#endif

#if CXIMAGE_SUPPORT_JPG

CXIMAGE_FORMAT_JPG, // JPG图像格式

#endif

CMAX_IMAGE_FORMATS // CImageEx类支持的图象类型的总数

};

//// GeoView_Enum_Mark_End

/////////////////////////////////////////////////////////////////////

    3、常量或宏规定:

/////////////////////////////////////////////////////////////////////

//// GeoView_Const_Mark_Begin

#define D3DFW_FULLSCREEN 0x00000001 // 全屏模式

#define D3DFW_STEREO 0x00000002 // 立体模式

#define D3DFW_ZBUFFER 0x00000004 // 使用Zbuffer

#define D3DFW_NO_FPUSETUP 0x00000008 // 不使用缺的DDSCL_FPUSETUP标志

const float PI = 3.14159265358979323846f; // Pi

#define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } } // 删除指针

//// GeoView_Const_Mark_End

/////////////////////////////////////////////////////////////////////

》》》对软件开发的理解和认识

给我留言

留言无头像?