华为C语言通用编程规范.pdf
章节 内容_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _0 前言 目的重点关注约定例外1 原则 好代码的原则类和函数设计指导原则保证静态类型安全遵循C+ISO标准优先编译时检查错误使用命名空间来限定作用域优先使用C+特性而不 是 C 特性2 命名 通用命名文件命名函数命名类型命名变量命名宏、常量、枚举命名3 格式 行宽缩进大括号函数声明和定义函数调用if语句循环语句switch语句表达式变量赋值初始化指针和引用编译预处理空格和空行类4 注释 注释风格文件头注释函数头注释代码注释5 头文件头文件职责头文件依赖6 作用域 命名空间全局函数和静态成员函数全局变量全局常量和静态成员常量7 类 构造、拷贝构造、赋值和析构函数继承多重继承重载8 函数 函数设计内联函数函数参数9 C+其 常量与初始化表达式类型转换资源分配和释放标准库const的用法异常他特性 模板宏10现代 代码简洁性和安全性提升智能指针Lambda接口C+特性0前言目的规则并不是完美的,通过禁止在特定情况下有用的特性,可能会对代码实现造成影响。但是我们制定规则的目的 为了大多数程序员可以得到更多的好处如果在团队运作中认为某个规则无法遵循,希望可以共同改进该规则。参考该规范之前,希望您具有相应的C+基础能力,而不是通过该文档来学习C+。1.了解C+的 ISO标准;2.熟知C+的基本语言特性,包 括 C+03/11/14/17相关特性;3.了解C+的标准库;重点关注1 约定C+的编程风格,比如命名,排版等。2 C+的模块化设计,如何设计头文件,类,接口和函数。3 C+相关特性的优秀实践,比如常量,类型转换,资源管理,模板等。4 现代C+的优秀实践,包 括 C+11/14/17中可以提高代码可维护性,提高代码可靠性的相关约定。约定规则:编程时必须遵守的约定(must)建议:编程时应该遵守的约定(should)本规范适用通用C+标准,如果没有特定的标准版本,适用所有的版本(C+03/ll/14/17)o例外无论是 规则 还是 建议,都必须理解该条目这么规定的原因,并努力遵守。但是,有些规则和建议可能会有例外。在不违背总体原则,经过充分考虑,有充足的理由的前提下,可以适当违背规范中约定。例外破坏了代码的一致性,请尽量避免。规则 的例外应该是极少的。下列情况,应风格一致性原则优先:修改外部开源代码、第三方代码时,应该遵守开源代码、第三方代码已有规范,保持风格统一。某些特定领域,优先参考其行业规范。1原则好代码的原则我们参考Kent Beck的简单设计四原则来指导我们的如何写出优秀的代码,如何有效地判断我们的代码是优秀的。1.通过所有测试(Passes its tests)2.尽可能消壁重复(Minimizes duplication)3,尽可能清口析表达(Maximizes clarity)4.更少代码元素(Has fewer elements)5.以上四个原则的重要程度依次降低。这组定义被称做简单设计原则。第一条强调的是外部需求,这是代码实现最重要的;第二点就是代码的模块架构设计,保证代码的正交性,保证代码更容易修改;第三点是代码的可阅读性,保证代码是容易阅读的;最后一点才是保证代码是简洁的,在简洁和表达力之间,我们更看重表达力。类和函数设计指导原则C+是典型的面向对象编程语言,软件工程界已经有很多OOP原则来指导我们编写大规模的,高可扩展的,可维护性的代码:-高内聚,低耦合的基本原则-SOLID原 则-迪 米 特 法 则-Tell,Dontask”原 则-组合/聚合复用原则保证静态类型安全我们希望C+应该是静态类型安全的,这样可以减少运行时的错误,提高代码的健壮性。但是由于C+的下面的特性存在,会 破 坏C+静态类型安全,我们针对这部分特性要仔细处理。unions联合体-类型转换cast-缩窄转换narrowingconversions-类型退化type decay-范围错误range errors-void*类型指针我们可以通过约束这些特性的使用,或者使用C+的新特性,比 如 variant(C+17,GSL的 span,narrow_cast等来解决这些问题,提 高 C+代码的健壮性遵 循 C+I S O 标准希望通过使用ISO C+标准的特性来编写C+代码,对 于 ISO标准中未定义的或者编译器实现的特性要谨慎使用,对 于 GCC等编译器的提供的扩展特性也需要谨慎使用,这些特性会导致代码的可移植性比较差。注意:如果模块中需要使用相关的扩展特性来,那么尽可能将这些特性封装成独立的接口,并且可以通过编译选项关闭或者编译这些特性。对于这些扩展特性的使用,请模块制定特性编程指南来指导这些特性的使用。优先编译时检查错误通过编译器来优先保证代码健壮性,而不是通过编写错误处理代码来处理编译就可以发现的异常,比如:通过const来保证数据的不变性,防止数据被无意修改。通 过 gskspan等来保证char数组不越界,而不是通过运行时的length检查。通过static_assert来进行编译时检查。使用命名空间来限定作用域全局变量,全局常量和全局类型定义由于都属于全局作用域,在项目中,使用第三方库中容易出现冲突。命名空间将作用域细分为独立的,具名的作用域,可有效地防止全局作用域的命名冲突。1.class,struct等都具有自己的类作用域。2.具名的namespace可以实现类作用域更上层的作用域。3.匿名namespace和 static可以实现文件作用域。对于没有作用域的宏变量,宏函数强烈建议不使用。作用域的一些缺点:1.虽然可以通过作用域来区分两个命名相同的类型,但是还是具有迷惑性。2.内联命名空间会让命名空间内部的成员摆脱限制,让人迷惑。3.通过多重嵌套来定义namespace,会让完整的命名空间比较冗长。所以,我们使用命名空间的建议如下:-对于变量,常量和类型定义尽可能使用namespace,减少全局作用域的冲突不要在头文件中使用using namespace-不要使用内联命名空间鼓励在.cpp文件中通过匿名namespace或者static秦封装,防止不必要的定义通过API暴露出去。优 先 使 用C+特 性 而 不 是C特性C+比起c 语言更加类型安全,更加抽象。我们更推荐使用C+的语言特性来编程,比如使用string而不是char*,使用vector而不是原生数组,使 用 namespace而不是static o2命 名通用命名常见命名风格有:驼峰风格(C amelC ase)大小写字母混用,单词连在一起,不同单词间通过单词首字母大写来分开。按连接后的首字母是否大写,又分:大驼峰(UperCamelCase)和小驼峰(lowerCamelCase)内核风格(unix_ like)单词全小写,用下划线分割。如:tesjresult匈牙利风格在 大驼峰 的基础上,加上前缀;前缀用于表达类型或用途。如:ruiSavedCount bTested1规 则2.1.1标识符命名使用驼峰风格不考虑匈牙利命名,在内核风格与驼峰风格之间,根据存量代码的情况,我们选择驼峰风格。类型命名风格类类型,结构体类型,枚举类型,联合体类型等类型定义大驼峰函数(包括全局函数,作用域函数,成员函数)大驼峰(接口部分可加前缀,如 XXX一函数名)全局变量(包括全局和命名空间域下的变量,类静态变量),局部变量,函数参数,类、结构体和联合体中的成员变量小驼峰常量(const),枚举值k+大小写混合宏大写+下划线命名空间全小写注意:上表中一常量一是指全局作用域、namespace域、类的静态成员域下,以const或 constexpr修饰的基本数据类型、枚举、字符串类型的变量。上表中变量是指除常量定义以外的其他变量,均使用小驼峰风格。文件命名建 议 2.2.1 C+文件以.cpp结尾,头文件以.h结尾我们推荐使用.h作为头文件的后缀,这样头文件可以直接兼容C和 C+。我们推荐使用.cpp作为实现文件的后缀,这样可以直接区分C+代码,而不是C代码。目前业界还有一些其他的后缀的表示方法:头文件:.hh,.hpp,.hxx cpp 文件:.cc,.cxx,.C对于本文档,我们默认使用.h和.cpp作为后缀。建 议 2.2.2 C+文件名和类名保持一致C+的头文件和cpp文件名和类名保持一致,使用下划线小写风格。如下:-database_connection.h-database_connection.cpp结构体,命名空间,枚举等定义的文件名类似。函数命名函数命名统一使用大驼峰风格,一般采用动词或者动宾结构。接口部分可加前缀,如 XXX一函数名。class List public:void AddElement(const Elements element);Element GetElement(const unsigned in t index)const;bool IsEmpty()const;bool MCC_GetClass();;namespace u tils voidDeleteUser();类型命名类型命名采用大驼峰命名风格。所有类型命名一一类、结构体、联合体、类型定义(typedef)、枚举-使用相同约定,例如:/cLasseSj structs and unionsclass UrlTable .class UrlTableTester .stru ct UrlTableProperties union Packet ./typedefstypedef std:m ap PropertiesMap;/enumsenum UrlTableErrors .对于命名空间的命名,建议全小写:/namespacenamespace o s u tils namespace f ile u tils )建 议2.4.1避免滥用typedef或者#define对基本类型起别名除有明确的必要性,否则不要用typedef/#define对基本数据类型进行重定义。优先使用头文件中的基本类型:有 符 号 类 型 无 符 号 类 型 描 述int8_tuint8_t宽度恰为8的有/无符号整数类型intl6_tuintl6_t宽度恰为1 6的有/无符号整数类型int32_tuint32_t宽度恰为3 2的有/无符号整数类型int64_tuint64_t宽度恰为6 4的有/无符号整数类型intptr_tuintptr_t足以保存指针的有/无符号整数类型如果模块有自己的定义,请使用统一的typedef来定义类型:typedef signed char V0S_INT8;typedef unsigned char VOS_UINT8;#if_WORDSIZE=64typedef unsigned long in t VOS_UINTPTR;#elsetypedef unsigned in t VOS_UINTPTR;#endif如果模块为了封装某个类型的信息,方便后续的扩展,可以使用typedef来重新定义。typedef uint8_t DevicelD;/./若干版本后扩展成16-bittypedef uintl6_t DevicelD;有特殊作用的类型typedef void*Handle;注意:不要使用#define进行别名定义,并且在C+11以后推荐使用using来定义类型。除上述理由外,应避免给其本数值类型别名定义。因为类型别名可读性并不好,隐藏了基本数值类型信息,如位宽,是否带符号。滥用举例:typedef uintl6_t MyCounter;/.in t Foo()MyCounterc;while(c=0)p rin tf(counter=%dn”,c);/.)/.对,MyCounter,是否可能小于0,打印时用d,还是,u,都不是很直观,极容易引入上述类似缺陷。变量命名通用变量命名采用小驼峰,包括全局变量,函数形参,局部变量,成员变量。std:string tableName;/Good:推荐此风格s td:string tablename;/B ad:禁Ik此风格std:string path;/Good:乂仃个单词时,小驼峰为全小”规 则 2 5 1 类的成员变量命名使用小驼峰。class Foo private:std:string fileName;/不添加任何作用域前缀或者,缀;当构造函数参数和成员变量重名时,可 通 过 this,来引用成员变量。class MyClass public:MyClass(int myVar):myVar(myVar)/O K,初始化列表允许同名入参初始化同名成员if(NeedNewVar()this-myVar=GetValue();/;匕 点不吸漏掉,否则就成了给人冬赋值)private:int myVar;);宏、常量、枚举命名宏采用全大写,下划线连接的格式。常量、枚举值使用k+大小写混合。函数局部const常量和类的普通const成员变量,使用小驼峰命名风格。#define MAX(a,b)(a)paramName2);/Good:函数参数放在一行ReturnType result=FunctionName(paramNamel,paramName2j/Good:保持与上方参数对齐paramName3);ReturnType result=FunctionName(paramNamel,paramName2,paramName3,paramName4?paramName5);/Good:参数换行,4空格缩进ReturnType result=VeryVeryVeryLongFunctionName(/行宽不满足第1个参数,直接换行paramNamel,paramName2,paramName3);/换行后,4空格缩进如果函数调用的参数存在内在关联性,按照可理解性优先于格式排版要求,对参数进行合理分组换行。/Good:每行的参数代表一组相关性较强的数据结构,放在一行便于理解int result=DealWithStructureLikeParams(left.x left.y,/表组.相关参数right.x,right.y);/表示另外一组相关参数if语句规 则 3.6.1 if语句必须要使用大括号我们要求if语句都需要使用大括号,即便只有一条语句。理由:-代码逻辑直观,易读;-在已有条件语句代码上增加新代码时不容易出错;-对 于 在if语句中使用函数式宏时,有大括号保护不易出错(如果宏定义时遗漏了大括号)。if(objectlsNotExist)/Good:单行条件语句也加大括号return CreateNewObject();)规 则 3.6.2禁 止 if/else/elseif写在同一行条件语句中,若有多个分支,应该写在不同行。如下是正确的写法:if(someConditions)DoSomething();else /Good:else 与 if 在不同行下面是不符合规范的案例:if(someConditions)else /Bad:eLse 与 if 在同 行循环语句规 则 3.7.1循环语句要求使用大括号和if语句类似,我们要求for/while循环语句必须加上的大括号,即使循环体是空的,或者循环语句只有一条。for(int i=0;i someRange;i+)DoSomething();)如果循环体是空的,应该使用空的大括号,而不是使用单个分号。单个分号容易被遗漏,也容易被误认为是循环语句中的一部分。for(int i=0;i someRange;i+)/Good:for 循环体足 1,使用大括号,而不是使用分号while(someCondition)/Good:while循环体是空,使用大括号,而不是使用分号while(someCondition)continue;/Good:continue太小不逻辑,可以使用大括3也可以不使用)坏的例子:for(int i=0;i threshold&/Good:换行后,逻辑操作符放在行尾someConditionsion)DoSomething();.int result=reallyReallyLongVariableNamel+/GoodreallyReallyLongVariableName2;表达式换行后,注意保持合理对齐,或 者 4 空格缩进。参考下面例子int sum=longVaribleNamel+longVaribleName2+longVaribleName3+longVaribleName4+longVaribleName5+longVaribleName6;/Good:4空格缩进int sum=longVaribleNamel+longVaribleName2+longVaribleName3+longVaribleName4+longVaribleName5+longVaribleName6;/Good:保持对齐变量赋值规 则 3.10.1多个变量定义和赋值语句不允许写在一行每行只有一个变量初始化的语句,更容易阅读和理解。int maxCount=10;bool isCompleted=false;下面是不符合规范的示例:int maxCount=10;bool isCompleted=false;/Bad:多个变量初始化需要分开放在多行,每行一个变量初始化int x,y=0;/Bad:多个变量定义需要分行,每行一个int pointX;int pointY;pointX=1;pointY=2;/Bad:多个变量赋值语句放同一行例外:for循环头、if初始化语句(C+17)、结构化绑定语句(C+17)中可以声明和初始化多个变量。这些语句中的多个变量声明有较强关联,如果强行分成多行会带来作用域不一致,声明和初始化割裂等问题。初始化初始化包括结构体、联合体、及数组的初始化规 则 3.11.1初始化换行时要有缩进,并进行合理对齐结构体或数组初始化时,如果换行应保持4 空格缩进。从可读性角度出发,选择换行点和对齐位置。const int rank=16,16,16,16,32,32,32,32,64,64,64,64,32,32,32,32;指针与引用建 议3.12.1指针类型“*跟随变量名或者类型,不要两边都留有或者都没有空格指针命名:*靠左靠右都可以,但是不要两边都有或者都没有空格。int*p=NULL;/Goodint*p=NULL;/Goodint*p=NULL;/Badint*p=NULL;/Bad例外:当变量被const修饰时,*”无法跟随变量,此时也不要跟随类型。char*const VERSION=V100;建 议3.12.2引用类型“&跟随变量名或者类型,不要两边都留有或者都没有空格引用命名:&靠左靠右都可以,但是不要两边都有或者都没有空格。int i=8;int&p=i;/Goodint&p=i;/Goodint&p=i;/Badint&p=i;/Bad编译预处理规 则3.13.1编译预处理的#统一放在行首,嵌套编译预处理语句时,“留 不缩进编译预处理的“#统一放在行首,即使编译预处理的代码是嵌入在函数体中的,“#也应该放在行首。#if defined(_x86_64_)&defined(_GCC_HAVE_SYNC_C0MPARE_AND_SWAP_16)/Good:放荏行音#define ATOMIC_X86_HAS_CMPXCHG16B 1/Good:放在行首#else#define ATOMIC_X86_HAS_CMPXCHG16B 0#endifint FunctionName()if(someThingError)#ifdef HAS_SYSLOG/Good:即使在函数内部,“井”也放在行首WriteToSysLog();#elseWriteToFileLog();#endif)内嵌的预处理语句#不缩进#if defined(_x86_64_)&defined(_GCC_HAVE_SYNC_COMPARE_AND_SWAP_16)#define AT0MIC_X86_HAS_CMPXCHG16B 1/Good:卤各层次,他亍的读#else#define AT0MIC_X86_HAS_CMPXCHG16B 0#endif空格和空行建 议 3.14.1水平空格应该突出关键字和重要信息,避免不必要的留白水平空格应该突出关键字和重要信息,每行代码尾部不要加空格。总体规则如下:i switch,case,do,while,for 等关键字之后加空格;小括号内部的两侧,不要加空格;大括号内部两侧有无空格,左右必须保持一致;一元操作符(&*+-!)之后不要加空格;二元操 作 符(=+-*/%|&八=!=)左右两侧加空格 三目运 算 符(?:)符号两侧均需要空格 前置和后置的自增、自 减(+-)和变量之间不加空格 结构体成员操作符(.-)前后不加空格 逗号(,)前面不加空格,后面增加空格 对于模板和类型转换()和类型之间不要添加空格 域操作符(:)前后不要添加空格 冒号(:)前后根据情况来判断是否要添加空格常规情况:void Foo(int b)/Good:大括号前应该留空格int i=0;/Good:变量初始化时,=前后应该有空格,分号前面不要留空格int bufkBufSize=0;/Good:大括号内两侧都无空格函数定义和函数调用:int result=Foo(argl,arg2);z/Bad:逗号后面需要增加空格int result=Foo(argl,arg2);八 八/Bad:函数参数列表的左括号后面不应该有个格,右括号前面不应该有空格指针和取地址x=*p;/Good:*操作符和指针p之间不加空:做=&x;/Good:&操作符和变量x之 间 不 加x=r.y;/Good:通过.访问成员变量时不加空格x=r-y;/Good:通过-访问成员变量时不加空格操作符:x=0;/Good:赋值操作的=前后都要加空格x=-5;/Good:负数的符号和数值之前不要加空格+X;/Good:前置和后置的+/一和变量之间不要加空格X-;if(x&!y)/Good:伍尔操作符前后要加上空格,!操作和变量之间不要空格v=w*x +y/z;/Good:二元操作符前后要加空格v=w*(x+z);/Good:括号内的友达式前后不需要加空格int a=(x y)?x:y;/Good:三 目 运 算 符,?和:前后需要添加空格循环和条件语句:if(condition)/Good:i f关键字和括号之间加空格,括号内条件语句前后不加空格 else /Good:eLse关键字和大括号之间加空格while(condition)/Good:whiLe关键字和括号之间加空格,括号内条件语句前后不加空格for(int i=0;i someRange;+i)/Good:for 关健字和括号之间加空格,分号之后加空格switch(condition)/Good:switch 关键字后面有 1 空格case 0:/Good:case语句条件和同号之间不加不,格break;default:break;模板和转换/尖括号()不与空格紧邻,和(之间也没有.vector x;y=static_cast(x);/在类型与指针操作符之间留空格也可以,但要保持一致.vector x;域操作符std:cout;/Good:命名空间访问,不要留空格int MyClass:GetValue()const /Good:对于成员函数定义,不要留空格冒号/添加空格的场景/Good:类的派生需要留有空格class Sub:public Base;/构造函数初始化列表需要留有空格MyClass:MyClass(int var):someVar(var)DoSomething();)/位域表示也留有空格struct XX char a:4;char b:5;char c:4;/不添加空格的场景/Good:对 于public:,private:这种类访问权限的冒号不用添加空格class MyClass public:MyClass(int var);private:int someVar;;/对于switch-case的case和default后面的冒号不用添加空格switch(value)case 1:DoSomething();break;default:break;)注意:当前的集成开发环境(IDE)可以设置删除行尾的空格,请正确配置。建 议3.1 4.2合理安排空行,保持代码紧凑减少不必要的空行,可以显示更多的代码,方便代码阅读。下面有一些建议遵守的规则:-根据上下内容的相关程度,合理安排空行;-函数内部、类型定义内部、宏内部、初始化表达式内部,不使用连续空行-不使用连续3个空行,或更多-大括号内的代码块行首之前和行尾之后不要加空行。int Foo()/Bad:两个函数定义间超过了一个空行int Bar()if(.)/Bad:大括号内的代码块行首不要加入空行/Bad:大括号内的代码块行尾不要加入空行int Foo(.)/Bad:函数体内行首不要加空行类规则3.15.1类访问控制块的声明依次序是public:,protected:,private:,每个都缩进1 个空格class MyClass:public BaseClass public:/注意没有缩进MyClass();/标准的4空格缩进explicit MyClass(int var);-MyClass()void SomeFunction();void SomeFunctionThatDoesNothing()void SetVar(int var)someVar=var;int GetVar()const return someVar;private:bool SomeInternalFunction();int someVar;int someOtherVar;在各个部分中,建议将类似的声明放在一起,并且建议以如下的顺序:类型(包括typedef,using和嵌套的结构体与类),常量,工厂函数,构造函数,赋值运算符,析构函数,其它成员函数,数据成员。规 则 3.15.2构造函数初始化列表放在同一行或按四格缩进并排多行/如果所有变量能放在同一行:MyClass:MyClass(int van):someVar(var)DoSomething();)/如果不能放在同一行,/必须置于冒号后,用维迸4个空格MyClass:MyClass(int var):someVar(var)someOtherVar(var+1)/Good:逗号后面留有空格DoSomething();/如果初始化列表需要置于多行,需要逐行对齐MyClass:MyClass(int var):someVar(var),/缩进4 个空格someOtherVar(var+1)DoSomething();)4注释一般的,尽量通过清晰的架构逻辑,好的符号命名来提高代码可读性;需要的时候,才辅以注释说明。注释是为了帮助阅读者快速读懂代码,所以要从读者的角度出发,按需注释“注释内容要简洁、明了、无二义性,信息全面且不冗余。注释跟代码一样重要。写注释时要换位思考,用注释去表达此时读者真正需要的信息。在代码的功能、意图层次上进行注释,即注释解释代码难以表达的意图,不要重复代码信息。修改代码时,也要保证其相关注释的一致性。只改代码,不改注释是一种不文明行为,破坏了代码与注释的一致性,让阅读者迷惑、费解,甚至误解。注释风格在 C+代码中,使用/*/和都是可以的。按注释的目的和位置,注释可分为不同的类型,如文件头注释、函数头注释、代码注释等等;同一类型的注释应该保持统一的风格。注意:本文示例代码中,大量使 用 后置注释只是为了更精确的描述问题,并不代表这种注释风格更好。文件头注释规 则4.2.1文件头注释必须包含版权许可/*Copyright(c)2019 name of copyright hoLder*Software Name is Licensed under the Mu Lan PSL vl.*You can use this software according to the terms and conditions of the Mu Lan PSL vl.*You may obtain a copy of Mu Lan PSL vl at:*http:/License.coscL SOFTWARE IS PROVIDED ON AN AS IS BASIS,WITHOUT WARRANTIES OFANY KIND,EITHER EXPRESS OR*IMPLIED,INCLUDING BUT NOT LIMITED TO N0N-7NFR工NGEMENT,MERCHANTABILITY OR FIT FOR A PARTICULAR*PURPOSE.*See the Mu Lan PSL vl for more detaiLs.*/函数头注释规 则 4 3 1 禁止空有格式的函数头注释并不是所有的函数都需要函数头注释;函数签名无法表达的信息,加函数头注释辅助说明;函数头注释统一放在函数声明或定义上方,使用如下风格之一:使用写函数头/单行函数头int Funcl(void);/多行函数头/第二行int Func2(void);使用/*/写函数头/*单行函数头*/int Funcl(void);/*另一种单行函数头*/int Func2(void);/*多行函数头*第二行*/int Func3(void);函数尽量通过函数名自注释,按需写函数头注释。不要写无用、信息冗余的函数头;不要写空有格式的函数头。函数头注释内容可选,但不限于:功能说明、返回值,性能约束、用法、内存约定、算法实现、可重入的要求等等。模块对外头文件中的函数接口声明,其函数头注释,应当将重要、有用的信息表达清楚。例:*返回实际写入的字节数,-1表示写入失败*注意,内 存b u f由调用者负责释放*/int WriteString(const char*buf,int len);坏的例子:/*函数名:lAlriteString*功能:写入字符串*参数:*返回值:*/in t W riteS tring(const char*buf,in t le n);上面例子中的问题:参数、返回值,空有格式没内容 函数名信息冗余 关键的b u f由谁释放没有说清楚代码注释规 则 4.4.1代码注释放于对应代码的上方或右边规 则 4.4.2注释符与注释内容间要有1 空格;右置注释与前面代码至少1 空格代码上方的注释,应该保持对应代码一样的缩进。选择并统一使用如下风格之一:使用/这是单行注释DoSomething();/这是多行注释/第二行DoSomethingO;使用/*/*这是单行注释*/DoSomething();/*另一种方式的多行注释*第二行*/DoSomething();代码右边的注释,与代码之间,至 少 留1空格,建议不超过4空格。通常使用扩展后的TAB键即可实现1-4空格的缩进。选择并统一使用如下风格之一:int foo=100;/放右边的注释int bar=200;/*放右边的注释:*/右置格式在适当的时候,上下对齐会更美观。对齐后的注释,离左边代码最近的那一行,保 证 1-4空格的间隔。例:const int kConst=100;/*相关的同类注释,可以考虑上下对齐*/const int kAnotherConst=200;/*上卜对,齐时,与左侧代码保持间隔*/当右置的注释超过行宽时.,请考虑将注释置于代码上方。规 则4.4.3不用的代码段直接删除,不要注释掉被注释掉的代码,无法被正常维护;当企图恢复使用这段代码时,极有可能引入易被忽略的缺陷。正确的做法是,不需要的代码直接删除掉。若再需要时,考虑移植或重写这段代码。这里说的注释掉代码,包 括 用/*/和,还 包 括#ifO,#ifdef NEVER.DEFINED等等。建 议4.4.1正式交付给客户的代码不能包含TODO/TBD/FIXME注释TODO/TBD注释一般用来描述已知待改进、待补充的修改点FIXME注释一般用来描述已知缺陷它们都应该有统一风格,方便文本搜索统一处理。如:/TODOf):补充XX 处理/FIXME.XX 缺陷5头文件头文件职责头文件是模块或文件的对外接口,头文件的设计体现了大部分的系统设计。头文件中适合放置接口的声明,不适合放置实现(内联函数除外)。对 于 cpp文件中内部才需要使用的函数、宏、枚举、结构定义等不要放在头文件中。头文件应当职责单一。头文件过于复杂,依赖过于复杂还是导致编译时间过长的主要原因。建 议5.L1每一个.cpp文件应有一个对应的.h文件,用于声明需要对外公开的类与接口通常情况下,每个.cpp文件都有一个相应的.h,用于放置对外提供的函数声明、宏定义、类型定义等。如果一个.cpp文件不需要对外公布任何接口,则其就不应当存在。例外:程序的入口(如main函数所在的文件),单元测试代码,动态库代码。示例:/Foo.h#ifn d e f FOO_H#define FOO二Hclass Foo public:Foo();void Fun();private:in t value;;#endif/Foo.cpp#include Foo.h,namespace /G ood:对内函数的声明放在.cpp文件的头部,并声明为匿名namespace s ta tic限制其作用域void Bar()()void Foo:Fun()Bar();)头文件依赖规 则5.2.1禁止头文件循环依赖头文件循环依赖,指a.h包含b.h,b.h包含c.h,c.h包含a.h,导致任何一个头文件修改,都导致所有包含了 a.h/b.h/c.h的代码全部重新编译一遍。而如果是单向依赖,如a.h包含b.h,b.h包含c.h,而c.h不包含任何头文件,则修改a.h不会导致包含了 b.h/c.h的源代码重新编译。头文件循环依赖直接体现了架构设计上的不合理,可通过优化架构去避免。规 则5.2.2禁止包含用不到的头文件用不到的头文件被包含的同时引入了不必要的依赖,增加了模块或单元之间的耦合度,只要该头文件被修改,代码就要重新编译。很多系统中头文件包含关系复杂,开发人员为了省事起见,直接包含一切想到的头文件,甚至发布了一个g o d.h,其中包含了所有头文件,然后发布给各个项目组使用,这种只图一时省事的做法,导致整个系统的编译时间进一步恶化,并对后来人的维护造成了巨大的麻烦。规 则5.2.3头文件应当自包含简单的说,自包含就是任意一个头文件均可独立编译。如果一个文件包含某个头文件,还要包含另外一个头文件才能工作的话,给这个头文件的用户增添不必要的负担。示例:如果a.h不是自包含的,需要包含b.h才能编译,会带来的危害:每个使用a.h头文件的.cpp文件,为了让引入的a.h的内容编译通过,都要包含额外的头文件 b.h。额外的头文件b.h必须在a.h之前进行包含,这在包含顺序上产生了依赖。规则5.2.4头文件必须编写#define保护,防止重复包含为防止头文件被重复包含,所有头文件都应当使用#define保护;不要使用#pragma once定义包含保护符时,应该遵守如下规则:1)保护符使用唯一名称;2)不要在受保护部分的前后放置代码或者注释,文件头注释除外。示例:假 定 VOS工 程 的 timer模 块 的 tim er.h,其目录为VOS/include/timer/Timer.h,应按如下方式保护:#ifndef VOS_INCLUDE_TIMER_TIMER_H#define VOS二INCL