软件编码规范文档_第1页
软件编码规范文档_第2页
软件编码规范文档_第3页
软件编码规范文档_第4页
软件编码规范文档_第5页
已阅读5页,还剩12页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

./C++设计编码规V1.00〔试用版设备开发部黄焕斌目录重要提示4背景41文件组织规则41.1命名41.2目录41.3预处理41.4注释52代码组织规则62.1空行62.2换行62.3空格62.4对齐72.5就近原则72.6精简原则73命名规则93.1自注释93.2变量93.3函数93.4类型103.5宏103.6常量104设计规则114.1识别类和函数114.2构造函数114.3封装性124.4函数参数124.5函数返回值134.6契约134.7规模144.8名字空间144.9类型转换144.10常量144.11联144.12静态变量144.13编译依赖154.14可重入155存管理规则165.1模块化165.2静态分配165.3new/delete165.4有效性165.5正确释放165.6拷贝166注释规则176.1有效性176.2普通注释176.3Doxygen注释176.4定义176.5申明186.6模块187维护规则197.1消除警告197.2代码修改197.3标本兼治19参考资料20重要提示 本规中的示例代码都在表格框中显示,绿色的表格框表示正确的示例代码,红色的表格框表示不建议的示例代码。背景 C++是大华设备软件和平台软件开发的主要软件,在新的软件框架里,两种平台的组件甚至是共用的。统一的代码风格,良好的设计风格,有利于代码的实现和阅读,有利于减少代码错误和提高代码效率,能有效地促进技术的交流和发展。 常见的代码规都异常冗长,调调框框太多。本规力求以简明的容,概括一些重要的规则,将相似的规则进行提炼集中描述,并提供对照的示例代码加深理解。规的使用者花半个小时左右,就可以熟悉整个规。 所有大华基于新软件框架的底层组件,业务组件,应用组件都必须遵守此规。例外 本规是强制要求,不过有些情况例外:与第三方库有关的代码:比如stl,boost,json等等,使用、移植这些库时,相关的代码可以按照这些库的规。Windows代码:主要指基于公共软件框架,同时使用了非公共组件框架的其他API接口的组件,可以继续保留Windows的规。文件组织规则命名所有的目录和文件名使用大写字母开头的单词组合,目录词之间可以用空格分开。引用文件名时要严格区分大小写。与操作系统关系密切的工程的命名可以参考操作系统的规则。Timer.cpp //源文件Timer.h //头文件Font.bin //资源文件Config1 //配置文件目录一个大的工程是由多个组件或模块组成的,对于每个组件或模块,其代码应集中管理,并具备完整的设计文档和单元测试代码,用子目录分类存放。目录或文件说明Bin测试程序目录Doc设计文档目录Include依赖的组件头文件目录与本组件的外部接口头文件目录,映射到其他地址Lib不同平台生成的库和依赖的库文件目录Makefile.ConfigsMakefile的不同平台的配置文件目录Src源文件,部头文件Test单元测试代码MakefileallRules.mkMakefile文件,一次性编译Makefile.Configs目录下所有配置对应的库,测试程序预处理为了防止头文件被重复引用,使用ifndef/define/endif结构产生预处理块。预处理宏中的单词应与文件名基本一致。//文件名为Guard.h#ifndef__GUARD_H__#define__GUARD_H__//Guard类的定义...#endif//__GUARD_H__注释源文件和头文件的头部都应进行注释,列出svn文件ID,申明,文件描述<说明是什么模块或什么类对应的文件>,修改记录<修改时间、svn作者、修改容>,可使用va模板。对于新加入svn的文件,应该其文件属性的svn属性列表中加入<svn:keywords,Id>属性。不要和文件中的类或模块的注释混淆。////"$Id$"////Copyright<c>1992-2007,ZheJiangDahuaTechnologyStockCO.LTD.//AllRightsReserved.//// Description:// Revisions: Year-Month-DaySVN-AuthorModification//代码组织规则空行类、结构、联合、函数、枚举等定义结束后,应加空行。类定义部相关的成员变量或操作之间不加空行,其他地方应加空行。函数体,逻揖上密切相关的语句之间不加空行,其它地方应加空行。源文件和头文件末尾保留一个空行。classA{};classB;换行每行代码只写一条语句。拆分复杂的复合表达式。代码行长度控制在80左右。长表达式在低优先级操作符处拆分成新行,操作符放在新行之首。if<loadFile<m_customFilePath.c_str<>,m_stream> &&reader.parse<m_stream,m_configAll> &&m_configAll["Groups"].size<>>=1 &&m_configAll["Users"].size<>>=1>{ infof<"CUserManager::SetDefault<>applycustomconfig.\n">;}空格‘,’之后要留空格。如果‘;’不是一行的结束符号,其后要留空格。二目或三目操作符前后留空格。但"[]"、"."、"->"这类操作符和作用于分辨符"::"前后不加空格。修饰符‘*’和‘&’紧靠变量名,仅在前面加空格。char*name;int*x,y; //此处y不会被误解为指针for<inti=0;i<N;i++>{}对齐程序的分界符‘{’和‘}’应独占一行并且位于同一列,同时与引用它们的语句左对齐。{}之的代码块使用TAB缩进并对齐,换行后的代码块使用TAB缩进并对齐。代码前的注释应和所注释的代码对齐。//commentoffoovoidfoo<>{ dosomething}就近原则较为紧密的代码应尽可能相邻。变量应在在定义的同时初始化。C++函数中将局部变量的定义放在要使用它的代码前最近处。将类的public接口申明放在类定义的最前面。voidfoo<>{ inta=0; a++; … intb=0; b++; …}精简原则DRY<Don'tRepeatYourself>系统中的每一项知识都需具有单一的表示。KISS<KeepItSimpleStupid>保持尽量简单。类中多次使用定义的很长的类型,应在类定义中对该类型进行更简洁自定义。访问一个变量的表达式太长,应定义一个临时的指针或引用来替换它。函数中多次调用返回结果不变的其他函数,应使用临时变量保存结果。函数中多次引用单件,应使用临时变量甚至类的成员变量来保存单件的引用或指针。将if/else/return的组合改为return条件表达式组合。Json::Value&users=m_configAll["Users"];for<uinti=pos;i<users.size<>-1;i++>{ users[i]=users[i+1];}users.resize<users.size<>-1>;return<condition?x:y>;命名规则整体上采用Java的命名规则,提高拼写效率。自注释标识符应是有意义的单词或其组合。用约定俗成的词,除了通用的缩写,应使用全拼。拼写要正确,注意单复数,禁止使用拼音。用最短的长度表达最准确的信息。//smellyexampletypedefstructtagPICINFO_INJPG

{

char

jpglog[4];

//

int

type;

//类型0:通行1:超速<暂无效>

char

sbid[10];

//设备ID

int

xscd;

//形行驶ID

char

cphm[20];

//车牌

int

cplx;

//车牌类型

int

cpys;

//车牌颜色

char

tgsj[19];

//抓拍时间…

}PICINFO_INJPG;变量使用"名词"或者"形容词+名词",首个单词首字母小写开头,其他单词大写打头。全局变量加前缀"g_"。成员变量加前缀"m_",结构和联合的成员变量不用加。静态变量加前缀"s_",静态成员变量加"sm_"。floatvalue;floatoldValue;floatnewValue;函数使用"动词"或者"动词+名词"〔动宾词组,名词前可再加修饰词,首个单词首字母小写开头,其他单词大写打头。C-API函数最前面应该是是小写的模块名。类的成员函数应当只使用"动词",被省略掉的名词就是对象本身。voidsleep<>; //全局函int setValue<…>; //全局函数int getValue<…>; //全局函数voidglCallList<GLuintlist>;//OpenGLAPIbox->draw<>; //成员函数类型可直接使用bool,char等建类型,其他使用stdint.h定义的类型。算术自定义类型,类,结构,联合,枚举类型统一使用大写字母开头的单词组合。class类型的命名前加大写字母’C’;templateclass类型的命名前加大写字母’T’;接口类的命名前加大写字母’I’。在类中嵌套的定义有具体意义的类型,是代码更易读。typedefintDistance;unionAddress;enumMode;structUser;classCPacket;template<classT,classA=std::allocator<T>> classTCircularQueue;classIDevVideoEnc;宏全部使用大写字母,单词之间用下划线分开。给代码控制宏命名加上前缀,来分类不同用途的宏。以上都是指自定义的宏,不包括编译预定义宏。#defineJSON_VALUE_USE_INTERNAL_MAP1#defineJSON_USE_CPPTL1常量避免使用常量宏,不得不使用时,参考宏的命名规则。const,枚举值等常量,应尽量作为类的成员,命名方式和变量相同。#defineMAX_SIZE10//notsogoodclassA{ enum { maxSize=10 };}设计规则识别类和函数基本原则是高抽象性,高聚性,低耦合性。使类的接口完整并且最小。一个函数不要完成多个功能。一个变量也不能有多用途。把方法中的重复代码抽象成私有函数。尽量使用已有的函数或者标准库来实现新的函数。区分两个类"A是一个B"与"A是B的一部分"的关系,分别对应类的继承和聚合关系。//正确的设计,虽然代码冗长。

classHead

{

public:

voidLook<void>{m_eye.Look<>;}

voidSmell<void>{m_nose.Smell<>;}

voidEat<void>{m_mouth.Eat<>;}

voidListen<void>{m_ear.Listen<>;}

private:

Eyem_eye;

Nosem_nose;

Mouthm_mouth;

Earm_ear;

};如果允许Head从Eye、Nose、Mouth、Ear派生而成,那么Head将自动具有Look、Smell、Eat、Listen这些功能。示例十分简短并且运行正确,但是这种设计方法却是不对的。//功能正确并且代码简洁,但是设计方法不对。classHead:publicEye,publicNose,publicMouth,publicEar{};构造函数如果一个类可能有多个构造函数,应有公用的私有初始化函数对成员进行初始化。如果确定只有一个构造函数,应使用成员初始化列表进行初始化。并且初始化列表中成员列出的顺序和它们在类中声明的顺序相同。使用explicit关键字消除隐式转换classCString{//…explicitCString<intn>;//preallocatenbytesCString<constchar*p>;};CStrings1=‘a’;//error:noimplicitchar->CStringconversionCStrings2<10>;//ok:stringwithspacefor10characters将采用了单件实例的类或接口的构造函数和析构函数权限设置为private或protectedclassITimerManager{public: staticITimerManager*instance<>;protected: ITimerManager<>; virtual~ITimerManager<>;…析构函数确定基类有虚析构函数。拷贝函数与赋值函数为非POD类型〔plain-old-data,onlyints,chars,floats,orpointers,orarrays/structsofPOD类声明一个拷贝构造函数和一个赋值操作符。而且在operator=中,应返回*this的引用,对所有数据成员赋值,检查给自己赋值的情况。classCZString{public: CZString<constchar*cstr>; CZString<constCZString&other>; ~CZString<>; CZString&operator=<constCZString&other> { if<this==&other>return*this; CZStringtemp<other>; constchar*cstr=m_cstr; m_cstr=temp.m_cstr; temp.m_cstr=cstr; }private: constchar*m_cstr;}封装性不要重新定义继承而来的非虚函数、成员、函数的缺省参数值。避免出现public数据成员。单件模式对象构造函数应为私有类型。如果一个操作不会修改对象的属性,应该加const修饰,妥善处理const的传递性。将只与类有关的常量、类型的定义放在类的部。classA{public: intgetValue<>const;private: intvalue; classInfo { };}函数参数如果参数是一个对象,应改成传递其引用或者指针。如果参数是指针或引用,且仅作输入用,则应在类型前加const,注意值传递不用修饰。sizt_tstrlen<constchar*string>;CRect<constCPoint&point,constCSize&size>;不能计算指针形参对应的实参数组的大小〔已退化。函数的参数顺序应该是先输入参数,再是输出参数,同时数组的地址在前,长度在后。函数返回值不要返回栈对象的指针或引用,不要返回栈数组。Object*createObject<>{ Objectobject; return&object;}设计函数时,必须返回一个对象时不要试图返回一个引用。Object&createObject<>{ Objectobject; returnobject;}Object&createObject<>{ Object*object=newObject; return*object;}契约提供约束,宁可编译和时出错,也不要运行时出错。检查函数的前置条件,满足前置条件是调用者的责任,而被调用者假定它的前置条件已经满足。检查函数的后置条件,也就是函数返回之时哪些条件是调用者可以期望的。检查类的不变式,类的不变式确保类处于良好的状态中,一般提供一个成员函数如isValid<>在函数进入和退出时检查不变式。对于运行契约检查,一般使用assert,也可以采用日志记录,抛出异常等方式。///获取队首元素constT&front<>{ assert<m_size>0>; returnm_queue[m_front];}不要混淆运行契约与有效代码,运行契约仅检查参数的合法性,且不应修改契约外定义的变量。assert<--m_count>0>;规模函数参数个数尽量控制在5个之。函数代码尽量控制在200行代码之。每个类的平均方法数尽量控制在20个之。函数嵌套深度控制在6级之,减少没必要的递归嵌套。函数〔调度函数除外扇出控制在5个以。类型转换避免强制类型转换。避免隐藏类型转换。常量C++中使用const来定义常量,替换宏定义的常量。不能使用无意义的立即数。类中使用的常量应使用类的部定义的枚举类型的值。只能使用ascii字符的字符串常量,不能使用中文等特定语言的字符串常量。联C++中应该用联函数替换宏定义的代码段。对性能要求比较高的场合应使用联函数。使用C++标准库常用的联函数,比如max,min等。如果使用某函数的地方较多,而且函数体较大,不应使用联函数。解耦合解耦合设计到很多方面,包括模块的划分,头文件依赖,模块对外的接口必须有解耦合处理,隐藏部实现的细节,避免编译依赖。如果可以使用对象的引用和指针,就要避免使用对象本身。定义某个类型的引用和指针只会涉及到这个类型的声明。定义此类型的对象则需要类型定义的参与。尽可能使用类的声明,而不使用类的定义。因为在声明一个函数时,如果用到某个类,是绝对不需要这个类的定义的,即使函数是通过传值来传递和返回这个类。不要在头文件中再包含其它头文件,除非缺少了它们就不能编译。相反,要一个一个地声明所需要的类,让使用这个头文件的用户自己去包含其它的头文件,以使用户代码最终得以通过编译。使用句柄类<Handleclass>隐藏实现细节来实现解耦合。//编译器还是要知道这些类型名,因为Person的构造函数要用到它们classDate;

classAddress;

classPersonImpl;classPerson{

public:

Person<conststring&name,constDate&birthday,constAddress&addr>;

virtual~Person<>;

stringname<>const;

stringbirthDate<>const;

stringaddress<>const;

private:

PersonImpl*impl;

//指向具体的实现类};除了句柄类,另一选择是使Person成为一种特殊类型的抽象基类,称为协议类〔Protocolclass。和句柄类的用户一样,协议类的用户只是在类的接口被修改的情况下才需要重新编译。classPerson{

public:

virtual~Person<>;

virtualstringname<>const=0;

virtualstringbirthDate<>const=0;

virtualstringaddress<>const=0;

virtualstringnationality<>const=0;

};可重入对于可能被多个任务访问的资源,要使用互斥量保护,上层应用不应直接使用中断。保护的围要准确,一般使用不同的互斥量来保护不同的资源,如果整体的代价不高,也可以使用同一个互斥量。仔细考察资源之间依赖的情况,防止死锁。使用Guard〔守卫者来实现函数的保护,除非难以使用。通过逻辑的设计,能够保证多个线程访问同一资源不会发生冲突时,可以不保护。对于只需要自加和自减的变量可以使用AtomicCount来保证原子性。存管理规则模块化使用或者设计专有的存管理模块,而不是直接使用new/delete。给标准容器编写高效安全的allocator。使用智能指针管理存或对象生命周期。静态分配在程序启动时从系统中静态分配好需要持续使用的存,提高性能。new/deleteC++中使用new/delete替换malloc/free。如果重载了operatornew就要同时重载operatordelete。有效性使用有效的指针及其所指向的空间,杜绝野指针。正确释放明确存块的所属对象及对象的生命周期,及时释放,防止存泄露。new出来的数组释放时也应表明它是数组。拷贝数组拷贝,或调用存拷贝、字符串拷贝接口时,应注意源区域和目标区域重叠的情况,参考memmove函数。注释规则有效性注释的容要清楚、明了,含义准确,防止注释二义性。在代码的功能、意图层次上进行注释,提供有用、额外的信息。对于已经充分自注释程度的代码无需注释。普通注释函数部使用C++注释风格,并在注释符号和注释容间留一个空格。注释容加在代码对象的上方或右方。if、for、do、while、case、switch、default等语句应该注释。未break的case段后面应该注释。代码块、预处理块较长的,应该注释。Doxygen注释函数外部使用DoxygenC++风格注释,并在注释符号和注释容间留一个空格。注释由简要注释和详细注释组成,两者都是可选的,且是两者都不能重复。简要注释使用一行C++注释,并在开始加一个额外的斜杠,使用\brief命令可以支持多行,简要注释的围将以空行或其他命令结束。详细注释使用至少两行C++注释,每行开始加一个额外的斜杠。注释块可以加在代码对象的上面或者右方,如果加在右方,还需要加额外的<符号,只有成员和参数的注释可以加在右边。一般使用简要注释即可,如需要更详细的说明的可以使用详细注释,详细注释应和其他注释用空行隔开。///Briefdescription.//////Detaileddescription.///somecodeitemheresomecodeitemhere///<Briefdescription.定义对于类,结构,联合,枚举,函数,宏,自定义类型,名字空间,非局部变量等定义均需要使用Doxygen简要注释。对于类,结构,联合,枚举等成员使用右置的Doxygen简要注释。如果变量是用来作为某种标志而不是连续的数值,

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论