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

下载本文档

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

文档简介

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.3 new/delete 165.4 有效性 165.5 对的释放 165.6 拷贝 166 注释规则 176.1 有效性 176.2 普通注释 176.3 Doxygen注释 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-2023,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);//preallocatenbytes CString(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简要注释。对于类,结构,联合,枚举等成员使用右置的D

温馨提示

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

评论

0/150

提交评论