编程规范的介绍.ppt

上传人:牧羊曲112 文档编号:6333780 上传时间:2023-10-17 格式:PPT 页数:81 大小:325KB
返回 下载 相关 举报
编程规范的介绍.ppt_第1页
第1页 / 共81页
编程规范的介绍.ppt_第2页
第2页 / 共81页
编程规范的介绍.ppt_第3页
第3页 / 共81页
编程规范的介绍.ppt_第4页
第4页 / 共81页
编程规范的介绍.ppt_第5页
第5页 / 共81页
点击查看更多>>
资源描述

《编程规范的介绍.ppt》由会员分享,可在线阅读,更多相关《编程规范的介绍.ppt(81页珍藏版)》请在三一办公上搜索。

1、,科技的实力、创新的活力、服务的诚信力,编程规范的介绍An Introduction to C/C+Coding Conventions广东天讯电信科技有限公司2007.4,2,编写的参考依据,1,Logiscope RuleChecker对代码规范作说明2,C+编码规范(陈世忠 Motorola)3,Write Solid Code(编程精粹)4,Manual of CVS,3,编程规范的作用,提高代码的质量提高编程的效率便于项目的管理如果一个项目坚持共同规范,那么会有下面这些好处:程序员可以理解任何代码,并知道正在做什么新使用C+的人可以很快驶入正轨新使用C+的人不必发展自己的风格并一直守

2、着它新使用C+的人不会一遍又一遍地反复犯相同的错误在一致的环境里大家可以少犯错误,4,一流代码的特性,鲁棒-Solid and Robust Code简洁-Maintainable and Simple Code高效-Fast Code简短-Small Code复用/共享-Re-usable Code可测试-Testable Code可移植-Portable Code(“写好代码的10个秘诀”微软,林斌),5,1.命名规范2.代码风格与版式3.有关注释4.函数5.类/结构6.变量、常量7.类型转换8.程序组织9.代码的管理,主要内容,6,1.命名规范,变量命、函数、类、宏、常量、类型、模版名和

3、文件名等都需要命名命名工作很重要命名的一些方法和原则my_namemyNamecMyName,7,1.1变量命名规则,变量的命名一般变量遵从 变量名=作用域+变量类型+变量含义静态变量遵从 变量名=”s_”+变量类型+变量含义,8,作用域的表示:,g_ 全局变量,如:g_lpImageInfo;m_ 类成员变量,如:m_strColorBuffer;sg_ 全局静态变量,如:sg_ucIterationsm_ 类成员静态变量,如:sm_pLengthEdits_ 类方法中静态变量,如:s_ pLengthEdit局部变量省略作用域,如:pLengthEdit,9,变量类型,10,1.1.3 变

4、量名字的含义,接近变量含义的英文单词,本着简单清楚、简单的原则,如Device表示设备,Name表示名字。若用多个单词表示各个单词应用大写字母打头,如DeviceName表示设备名。根据以上命名规则,我们对变量m_szCallingNumber应知道该变量是一个类的数据成员,该成员是零结尾的字符串,存储的是呼叫号码。这样我们就能望文生义,知道该变量的作用域、类型和含义,便于以后的维护和实际的编程。,11,注意事项,全局变量可在头文件中定义,并尽量减少全局变量的使用。对于多文件共享的全局变量尽量定义在独立的一个头文件中。全局变量会增大模块间的耦合。,12,1.2 函数命名规则,全局函数:Dll或

5、引出模块缩写”+“具体含义”,避免导出的全局函数与别人的重名;如Display_InitEditor()。类成员函数:省略作用域,直接用具体含义表示。以前缀 OnMsg作为类中针对消息的响应函数的前缀,前缀OnNotify作为对Notify消息的响应函数的前缀。,13,1.3类的命名规则,遵从:类名=“C”+类的含义,类的含义应为易于理解英文单词、术语或其简写,例如:class CClipCompileDlg:public CDYDialog 为便于界定,每个单词的首字母要大写。类的命名推荐用名词或形容词名词的形式,例如:CAnalyzer,CFastVector.,14,1.4宏,所有的宏在

6、头文件中定义。宏名由英文单词和下划线组成,字母皆大写。例如:#defineSET_MOUSE_POS 对于宏的展开部分,在宏的参数出现的地方要加括号“()”。保证宏替换的安全,同时提高代码的可读性。举例:/不要这样写#define GET_NAME(obj,ind)obj-nameind/应该这样写#define GET_NAME(obj,ind)(obj)-name(ind),15,1.5 常量,常量名由类型前缀全大写字母组成,单词间通过下划线来界定,a.全局常量全局常量在cpp中定义,如其他库需要使用时,在头文件中用extern.声明,保证只有一个实例。常量名“G_”+常量含义,其他同函数

7、内常量,如:#defineG_FilePath“C:My documentspic.jpg”b.类成员常量常量名“M_”+常量含义,其他同函数内常量由英文单词和下划线组成。全部字母大写。例如:M_SCREEN_WIDTH,C.原则上函数内不定义常量,16,1.6类型(自定义),类型名由英文单词组成(不用下划线),每个单词首字母大写。指针类型名前冠以“P”。typedef struct tagStructName int iType;LPCSTR pszCatName;LPCSTR pszITName;LPCSTR pszITDescription;STRUCTNAME,*PSTRUCTNAME

8、;,17,1.7关于定义struct、union变量,禁止在程序中直接定义结构体(struct)、联合体(union)变量。要使用typedef为自定义的结构体、联合体重新命名后,再使用这个名字来定义变量。理由:使代码更容易理解。举例:/不要这样写struct.varName;/应该这样写typedef struct.typeName;typeName varName;,18,2.代码风格与版式,一段稍长一点的无格式代码基本上是不可读的。一定形成良好的风格(style),19,2.1空行的使用,空行起着分隔程序段落的作用,提高代码的可读性。空行得体(不过多也不过少)将使程序的布局更加清晰。空行

9、不会浪费内存,虽然打印含有空行的程序是会多消耗一些纸张,但是值得。所以不要舍不得用空行。在每个类声明之后、每个函数定义结束之后都要加空行。在一个函数体内,逻揖上密切相关的语句之间不加空行,其它地方应加空行分隔。,20,2.2语句与代码行,一行代码只做一件事情,如只定义一个变量,或只写一条语句。这样的代码容易阅读,并且方便于写注释。“if”、“for”、“while”、“do”、“try”、“catch”等语句自占一行,执行语句不得紧跟其后。不论执行语句有多少都要加“”。这样可以防止书写失误。,21,2.3缩进和对齐,TAB和空格的问题程序的分界符 和 应独占一行并且位于同一列,同时与引用它们的

10、语句左对齐。之内的代码块在 右边一个制表符处左对齐。如果出现嵌套的,则使用缩进对齐。如果一条语句会对其后的多条语句产生影响的话,应该只对该语句做半缩进,以突出该语句。,22,例如:,Void Function(int x)CSessionLock iLock(*m_psemLock);for(初始化;终止条件;更新)/.try/.catch(const exception&err)/.catch(.)/./.,23,2.4代码行最大长度,代码行最大长度宜控制在70至80个字符以内代码行不要过长,否则眼睛看不过来,也不便于打印。,24,2.5长行拆分,长表达式要在低优先级操作符处拆分成新行,操作

11、符放在新行之首(以便突出操作符)。拆分出的新行要进行适当的缩进,使排版整齐,语句可读。例如:if(very_longer_variable1=very_longer_variable2),25,2.6空格的使用,关键字之后要留空格。象 const、virtual、inline、case 等关键字之后至少要留一个空格,否则无法辨析关键字。象 if、for、while、catch 等关键字之后应留一个空格再跟左括号(,以突出关键字。函数名之后不要留空格,紧跟左括号(,以与关键字区别。(向后紧跟。而)、,、;向前紧跟,紧跟处不留空格。,之后要留空格,如 Function(x,y,z)。如果;不是一行

12、的结束符号,其后要留空格,如 for(initialization;condition;update)。赋值操作符、比较操作符、算术操作符、逻辑操作符、位域操作符,如=、+=、=、+、*、%、&、|、,等二元操作符的前后应当加空格。,26,一元操作符如!、+、-、i+)和if(a=b)&(c=d),2.6空格的使用(续),27,2.6空格的使用(例子),void Func1(int x,int y,int z);/良好的风格void Func1(int x,int y,int z);/不良的风格/=if(year=2000)/良好的风格if(year=2000)/不良的风格if(a=b),28

13、,2.7 建议在表达式中使用括号,对于一个表达式,在一个二元、三元操作符操作的操作数的两边,应该放置“(”和“)”。避免出现不明确的运算、赋值顺序。举例:/下面这行代码:result=fact/100*number+rem;/最好写成这样result=(fact/100)*number)+rem;,29,2.8 不在条件判断语句中赋值,不要在控制语句if,while,for 和 switch的条件表达式中使用赋值操作符。赋值操作符包括:=,+=,-=,*=,/=,%=,=,=,&=,|=,=,+,-。理由:一个类似于 if(x=y)这样的写法是不明确、不清晰的,代码的作者也许是想写成这样:if

14、(x=y)。/do not writeif(x-=dx).,30,2.9 赋值表达式中的规定,在一个赋值表达式中:一个左值,在表达式中应该仅被赋值一次。对于多重赋值表达式,一个左值在表达式中仅应出现一次,不要重复出现。理由:避免产生不明确的赋值顺序。举例:/不要象下面这样写代码:i=ti+;/一个左值,在表达式中应该仅被赋值一次a=b=c+a;/对于多重赋值表达式,一个左值在表达式中仅应出现一次,不能重复出现。i=ti=15;/对于多重赋值表达式,一个左值在表达式中仅应出现一次,不能重复出现。,31,2.10使用正规格式的布尔表达式,对于if,while,for等控制语句的条件表达式,建议使用

15、正规的布尔格式。理由:使代码更容易理解。举例:/不要象下面这样写代码:if(test).for(i=1;function_call(i);i+).,32,/最好这样写:if(test=true).for(i=1;function_call(i)=true;i+).,33,3.有关注释,注释的位置应与被描述的代码相邻,可以放在代码的上方或右方,不可放在下方。边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。不再有用的注释要删除。注释应当准确、易懂,防止注释有二义性。错误的注释不但无益反而有害。当代码比较长,特别是有多重嵌套时,应当在一些段落的结束处加注释,便于阅读。,34,3

16、.1对函数进行注释,在函数的声明之前,要给出精练的注释(不必牵扯太多的内部细节),让使用者能够快速获得足够的信息使用函数。格式不做具体要求。在函数的定义之前,要给出足够的注释。注释格式要求如下:,35,/*【函数名称】(必需)【函数功能】(必需)【参数】(必需。标明各参数是输入参数还是输出参数。)【访问变量】(必需。列出该函数访问的全局变量、成员变量。【返回值】(必需。解释返回值的意义。)【使用情况】(必需。调用其它函数的情况,被调用的情况)【开发者及日期】(必需)【版本】(必需)【更改记录】(若有修改,则必需注明)*/,36,3.2对类进行注释,在类的声明之前,要给出足够而精练的注释。注释格

17、式要求如下:/*【类名】(必需)【功能】(必需)【接口说明】(必需)【开发者及日期】(必需)【版本】(必需)【版权信息】(可选)【更改记录】(若修改过则必需注明)*/,37,3.3其它应该考虑进行注释的地方,l变量的声明、定义。通过注释,解释变量的意义、存取关系等;例如:int m_nNumber;/记录图形个数。被SetDate()、GetDate()使用。,38,l数据结构的声明。通过注释,解释数据结构的意义、用途等;例如:/定义结构体,存储元件的端点。用于将新旧的端点对应。typedef struct int nBNN;int nENN;int nBNO;int nENO;Element

18、;l调用函数。通过注释,解释调用该函数所要完成的功能;例如:SetDate(m_nNumber);/设置当前的图形个数。,39,l分支。通过注释,解释不同分支的意义;例如:if(m_iShortRadio=0)/三相的情况 strvC.Format(%-10.6f,vC);straC.Format(%-10.6f,aC);else if(m_iShortRadio=1)/两相的情况 strvC=_T();straC=_T();l 赋值。通过注释,说明赋值的意义;例如:m_bDraw=true;/将当前设置为绘图状态,40,l 程序块的结束处。通过注释,标识程序块的结束。例如:if(name=W

19、hite)if(age=20)/年龄判断、处理结束/姓名判断、处理结束l其它有必要加以注释的地方;,41,3.6行末注释尽量对齐,建议同一个函数或模块中的行末注释应尽量对齐。nCount=0;/计数器,表示正在处理第几个数据块 BOOL bNeedSave;/是否保存从服务器返回的数据 BOOL bReturnCache;/是否将Cache中的内容返回客户端 DWORD BytesWritten;/写入的数据长度,42,4.函数,函数是程序执行的最小单位,任何一个有效的C/C+程序都少不了函数。,43,函数原型的格式为:存储类 返回值类型名空间或类:函数名(参数列表)const说明符 例如:s

20、tatic inline void Function1(void)int CSem:Function2(IN const char*pcName)const 其中:以 括住的为可选项目。除了构造/析构函数外,返回值类型 和 参数列表 项不可省略(可以为 void)。const说明符 仅用于成员函数中,4.1函数原型,44,函数声明的格式为:函数原型;例如:static void Function(void);函数声明和其它代码间要有空行分割。声明成员函数时,为了紧凑,返回值类型和函数名之间不用换行,也可以适当减少声明间的空行。,4.2函数声明,45,4.3参数描述宏,以下预定义宏对程序的编译没

21、有任何影响,只为了增强对参数的理解:宏说明IN输入参数OUT 输出参数OPTIONAL可选参数通常指可以为NULL的指针参数,带默认值的参数不需要这样标明RESERVED这个参数当前未被支持,留待以后扩展其中:除了空参数 void 以外,每个参数左侧都必须有 IN 和/或 OUT 修饰既输入又输出的参数应记为:IN OUT,而不是 OUT ININ/OUT的左侧还可以根据需要加入一个或多个上表中列出的其它宏参数描述宏的使用思想是:只要一个宏可以用在指定参数上(即:对这个参数来说,用这个描述宏修饰它是贴切的),那么就应当使用它。也就是说,应该把能用的描述宏都用上,以期尽量具体地描述一个参数。,4

22、6,4.4参数列表,参数列表的格式为:参数描述宏1 参数类型1 参数1,参数描述宏2 参数类型2 参数2,.例如:IN const int nCode,OUT string&nNameIN CDatabase*pDB,OPTIONAL IN OUT int*pRecordCount=NULLIN OUT string&stRuleList,IN int nOperate=0.其中:参数描述宏 见上文参数命名规范与变量的命名规范相同,47,4.5默认参数,类似地,参数的默认值只能出现在函数声明中,但是为了明确起见,这些默认值应以注释的形式在函数定义中给出。例如:Bool CStringEx:Re

23、gexFind(OUT VREGEXRESULT&vResult,IN stringEx stRegEx,IN size_t nIndex/*=0*/,IN size_t nStartPos/*=0*/,IN bool bNoCase/*=false*/,IN bool bNewLine/*=true*/,IN bool bExtended/*=true*/,IN bool bNotBOL/*=false*/,IN bool bNotEOL/*=false*/,IN bool bUsePerlStyle/*=false*/)const/.,48,4.9将重复使用的代码编写成函数,建议将重复使用

24、的简单操作编写成函数。对于重复使用的功能,虽然很简单,也应以函数的形式来处理,这样可以简化代码,使代码更易于维护。,49,4.10在函数调用语句中不要使用赋值操作符,函数调用语句中,在函数的参数列表中不要使用赋值操作符。赋值操作符包括=,+=,-=,*=,/=,%=,=,=,/注意这里!,50,4.11保护可重入函数中的全局变量,编写可重入函数时,若操作全局变量,则应加以保护。理由:如果全局变量不加以保护,当多个线程调用此函数时,很可能使此变量变为不可知状态。,51,4.12调用系统API,所有系统API调用前都要加上全局名称解析符:。例如::MessageBoxA(NULL,gcErrorM

25、sg,!FATAL ERROR!,MB_ICONSTOP|MB_OK);if(0=:GetTempFileName(m_basedir.c_str(),byT(bai),0,stR.ref()/.,52,5 类/结构,类是C+中最重要也是使用频率最高的新特性之一。类的版式好坏将极大地影响代码品质。,53,5.1 构造函数,为每一个类显式定义默认构造函数。理由:确保类的编写者考虑在类对象初始化时,可能出现的各种情况。举例:class CMyClass CMyClass();.;,54,5.2 拷贝构造函数,当类中包含指针类型的数据成员时,必须显式的定义拷贝构造函数。建议为每个类都显式定义拷贝构造

26、函数。理由:确保类的编写者考虑类对象在被拷贝时可能出现的各种情况。举例:class CMyClass.CMyClass(CMyClass,55,5.3 析构函数,明确提供类的析构函数确保类的编写者考虑类对象在析构时,可能出现的各种情况。/writeclass CMyClass.CMyClass(CMyClass,56,5.4 析构函数中的异常处理,在析构函数中要考虑发生异常的情况,要包含try、catch语句块。理由:防止terminate被调用,同时确保析构函数能被完整执行。(terminate函数:当我们没有为产生的某一种异常情况提供处理方法时,terminate会被调用。这个函数的作用正

27、如其名字所表示的它终止你程序的运行,而且是立即终止,并且不会释放局部对象。),57,5.5不要重新定义继承来的非虚函数,在派生类中不要对基类中的非虚函数重新进行定义。如果确实需要在派生类中对该函数进行不同的定义,那么应该在基类中将该函数声明为虚函数;理由:当通过一个指向对象的指针调用非虚成员函数时,最终调用哪个函数取决于指针本身的类型,而不是指针当前所指向的对象。,58,5.6 建议用内联函数代替宏函数,理由:同宏函数相比,内联函数不但具有宏函数的效率,而且使用起来更安全。,59,5.7 操作符“new”,“delete”的重载,如果你为一个类重载了操作符new,那你也应该为这个类重载操作符d

28、elete。理由:操作符new和操作符delete需要一起合作。,60,5.8类数据成员的访问控制,类对外的接口应该是完全功能化的,类中可以定义Public的成员函数,但不应该有Public的数据成员。理由:要想改变对象的当前状态,应该通过它的成员函数来实现,而不应该通过直接设置它的数据成员这种方法。一个类的数据成员应该声明为private的,最起码也应该是protected的。,61,5.9类成员的声明版式,对于比较复杂(成员多于20个)的类,其成员必须分类声明。每类成员的声明由访问说明符(public,private,或protected)+全行注释开始。如果一类声明中有很多组功能不同的成

29、员,还应该用分组注释将其分组。分组注释也要与 class 关键字对其。每个成员的声明都应该由“class”关键字开始向右缩进一个制表符,成员之间左对齐。例如:class CXXXpublic:/类型定义 typedef vector VSTR;public:/构造、析构、初始化 CXXX();CXXX();,62,public:/公共方法/功能组1 void Function1(void)const;long Function2(IN int n);/功能组1/功能组2 void Function3(void)const;bool Function4(OUT int,63,5.10初始化列表,

30、应当尽可能通过构造函数的初始化列表来初始化成员和基类。初始化列表至少独占一行,并且与构造函数的定义保持一个制表符的缩进。例如:CXXX:CXXXX(IN int nA,IN bool bB):m_nA(nA),m_bB(bB)/.;初始化列表的书写顺序应当与对象的构造顺序一致,即:先按照声明顺序写基类初始化,再按照声明顺序写成员初始化。如果一个成员 a 需要使用另一个成员 b 来初始化,则 b 必须在 a 之前声明,否则将会产生运行时错误(有些编译器会给出警告)。,64,例如:/.class CXXXX:public CAA,public CBB/.CYY m_iA;CZZ m_iB;/m_i

31、A必须在m_iB之前声明;CXXX:CXXXX(IN int nA,IN int nB,IN bool bC):CAA(nA),CBB(nB),m_iA(bC),m_iB(m_iA)/先基类,后成员,/分别按照声明顺序书写/.;,65,5.11明确指明派生类与基类的关系,在由基类派生子类时,要明确指明派生类对基类的访问控制(public,protected,private)。理由:明确子类、基类的继承关系。举例:/不要这样写class inherclass1:public Base1,Base2/等价于public Base1,private Base2.class inherclass2:B

32、ase1/等价于private Base1./应该这样写,class inherclass:public Base1,private Base2.class inherclass2:private Base1.,66,6 变量、常量,变量、常量的声明格式如下:存储方式 类型 变量名;其中:以 括住的为可选项目。存储方式 的说明见下文,67,6.1变量、常量的定义,变量、常量的定义格式如下:存储方式 类型 变量名=初始值;其中:以 括住的为可选项目。“存储方式”的说明见下文变量必须在使用前初始化(并不是每个编译器赋相同的初始值),68,6.2存储方式,除 auto 类型以外,诸如 extern,

33、static,register,volatile 等存储方式均不可省略,且必须在声明和定义中一致地使用(即:不允许仅在声明或定义中使用)。,69,7类型转换,禁止使用C风格的(类型)格式转换,应当优先使用C+的 xxx_cast 风格的类型转换。C+风格的类型转换可以提供丰富的含义和功能,以及更好的类型检查机制,这对代码的阅读、修改、除错和移植有很大的帮助。其中static_caststatic_cast用于编译器认可的,安全的静态转换,比如将 char 转为 int 等等。该操作在编译时完成reinterpret_castreinterpret_cast用于编译器不认可的,不安全的静态转换,

34、比如将 int*转为 int 等等。这种转换有可能产生移植性方面的问题,该操作在编译时完成const_castconst_cast用于将一个常量转化为相应类型的变量,比如将 const char*转换成 char*等等。这种转换通常伴随潜在的错误。该操作在编译时完成,70,7类型转换,dynamic_castdynamic_cast是C+RTTI机制的重要体现,用于在类层次结构中漫游。dynamic_cast可以对指针和引用进行自由度很高的向上、向下和交叉转换。被正确使用的dynamic_cast操作将在运行时完成.此外,对于定义了 单参构造函数 或 类型转换操作 的类来说,应当优先使用构造函

35、数风格的类型转换,如:string(test)等等。通常来说,xxx_cast 格式的转换与构造函数风格的类型转换之间,最大的区别在于:构造函数风格的转换通常会生成新的临时对象,可能伴随相当的时间和空间开销。而 xxx_cast 格式的转换只是告诉编译器,将指定内存中的数据当作另一种类型的数据看待,这些操作一般在编译时完成,不会对程序的运行产生额外开销。当然,dynamic_cast 则是一个例外。,8.程序组织,72,8.1一个头文件中只声明一个类,在一个头文件中,只应该包含对一个类的声明(嵌套类的情况除外)。头文件是指以.h、.hh、.H、.hxx、.hpp为后缀的文件。理由:提高代码的可

36、读性。,73,8.2 一个源文件中只实现一个类,在一个源文件中定义的每一个函数,都应该属于同一个类,即对一个类的实现描述要独占一个文件。源文件指以*.cc,*.cxx,*.cpp,*.C or*.c为后缀的代码文件。理由:提高代码的可读性。,74,8.3 头文件中只包含声明,不应包含定义,在头文件中只包含声明,不要包含全局变量和函数的定义内联函数的情况除外。,75,8.4 源文件中不要有类的声明,在源文件中只应该包含对类的实现,不应该包含任何类的声明。类声明应该统一放到头文件中去。源文件指以*.cc,*.cxx,*.cpp,*.C or*.c为后缀的代码文件。,76,8.5 可被包含的文件,为

37、改善程序代码的组织结构,只允许头文件被包含到其它的代码文件中去。,77,8.6 避免头文件的重复包含,头文件的格式应该类似于:#ifndef#define.#endif或者#if!defined()#define.#endif上面的是一个标识字符串,要求该标识字符串必须唯一。,78,9.代码的版本管理,如果项目有2个人以上参与,或者规模比较大,就一定要使用版本管理工具 推荐使用的版本管理工具CVS/SVNCVS的主要操作 update commit add remove tag 冲突的解决,79,Where Good Codes come from?,The pursuit of perfec

38、tion Responsible for your codeIts my baby The code is not written for yourselfTo work with other peopleTo share with other people Work hard,and work smartlyThink more,code lessTry to use more tools,80,There is no free meal!,Experience is more importantGood design comes from good designerSenior programmer:5 yearsTeam leader:10 years No end for perfection,81,谢 谢 大 家!Q&A,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号