Google C++ 编程规范

所需积分/C币:9 2018-08-21 16:58:41 478KB PDF
收藏 收藏
举报

Google C++ 编程规范 Google 经常会发布一些开源项目, 意味着会接受来自其他代码贡献者的代码. 但是如果代码贡献者的编程风格与 Google 的不一致, 会给代码阅读者和其他代码提交者造成不小的困扰. Google 因此发布了这份自己的编程风格指南, 使所有提交代码的人都能获知 Google 的编程风格.
15.命名空间格式化( Namespace Formatting)…mm48 16.水平留白( Horizontal whitespace)…....49 17.垂直留白( Vertical Whitespace n50 九、规则之例外…52 1.现有不统代码( Existing Non- conformant code)…-52 2. Windows代码( Windows code) ●●●●D鲁●●备●●●鲁命·●●●D●●●告●●●●● 52 十、团队合作……3 头文件 通常,每一个.CC文件(C++的源文件)都有一个对应的h文件(头文件),也有一些例 外,如单元测试代码和只包含main(的.CC文件。 正确使用头文件可令代码在可读性、文件大小和性能上大为改观。 下面的规则将引导你规避使用头文件时的各种麻烦。 1.# define的保护 所有头文件都应该使用# define防止头文件被多重包含( multiple inclusion),命名格式 当是:<PPO丿LCT><PAT<FIE>I 为保证唯一性,头文件的命名应基于其所在项日源代码树的全路径。例如,项日foo中的头 文件foo/src/bar/baz.h按如下方式保护: #ifndef foo bar baz h #define foo bar baz h #endif / Foo BAR BaZ- H 2。头文件依赖 使用前置声明( forward declarations)尽量减少.h文件中# include的数量。 当一个头文件被包含的同时也引入了一项新的依赖( dependency),只要该头文件被修改, 代码就要重新编译。如果你的头文件包含了其他头文件,这些头文件的任何改变也将导致那 些包含了你的头文件的代码重新编译。因此,我们宁可尽量少包含头文件,尤其是那些包含 在其他头文件中的 使用前置声明可以显著减少需要包含的头文件数量。举例说明:头文件中用到类File,但不 需要访问Flle的声明,则头文件中只需前置声明 class file;无需 include file/base/file.h"。 在头文件如何做到使用类FoO而无需访问类的定义? )将数据成员类型声明为Fo米或Foo& 2)参数、返回值类型为Foo的函数只是声明(但不定义实现); 3)静态数据成员的类型可以被声明为F0o,因为静态数据成员的定义在类定义之外 另·方面,如果你的类是Foo的了类,或者含有类型为Foo的非静态数据成员,则必须为 之包含头文件。 有时,使用指针成员( pointer members,如果是 scoped ptr更好)替代对象成员( object members)的确更有意义。然而,这样的做法会降低代码可读性及执行效率。如果仅仅为 了少包含头文件,还是不要这样替代的好 当然,,CC文件无论如何都需要所使用类的定义部分,自然也就会包含若干头文件。 译者注:能依赖声明的就不要依赖定义。 3.内联函数 只有当函数只有10行甚至更少时才会将其定义为内联函数( inline function)。 定义( Definition):当函数被声明为内联函数之后,编译器可能会将其内联展开,无需 按通常的函数调用机制调用内联函数。 优点:当函数体比较小的时候,内联该函数可以令目标代码更加高效。对于存取函数 ( accessor、 mutator)以及其他一些比较短的关键执行函数 缺点:滥用内联将导致程序变慢,內联有可能是目标代码量或増或减,这取决于被内联的函 数的大小。内联较短小的存取函数通常会减少代码量,但内联一个很大的涵数(译者注:如 果编译器允许的话)将戏剧性的增加代码量。在现代处理器上,山于更好的利用指令缓存 ( instruction cache),小巧的代码往往执行更快。 结论:一个比较得当的处理规则是,不要内联超过10行的函数。对于析构函数应慎重对待, 析构函数往往比其表面看起来要长,因为有一些隐式成员和基类析构函数(如果有的话)被 调用! 另一有用的处理规则:内联那些包含循环或 switch语句的函数是得不偿失的,除非在人多 数情况下,这些循环或 switch语句从不执行。 重要的是,虚函数和递归函数即使被声明为内联的也不一定就是内联函数。通常,递归函数 不应该被声明为内联的(译者注:递归调用堆栈的展开并不像循环那么简单,比如递归层数 在编译时可能是未知的,大多薮编译器都不支持内联递归函数)。析构函数内联的主要原因 是其定义在类的定义中,为了方便抑或是对其行为给出文档 4.-inLh文件 复杂的内联函数的定义,应放在后缀名为-inl.h的头文件中。 在头文件中给出内联函数的定义,可令编译器将其在调用处内联展开。然而,实现代码应完 仝放到.cc文件中,我们不希望.h文件中出现太多实现代码,除非这样做在可读性和效率上 有明显优势。 如果内联函数的定义比较短小、逻辑比较简单,其实现代码可以放在.h文件中。例如,存 取函数的实现理所当然都放在类定义中。出于实现和调用的方便,较复杂的内联函数也可以 放到h文件中,如果你觉得这样会使头文件显得笨重,还可以将其分离到单独的-n.h中。 这样即把实现和类定义分离开来,当需要吋包含实现所在的-inl.h即可。 -inl.h文件还可用于数模板的定义,从而使得模板定义可读性增强 要提醒的一点是,-inl.h和其他头文件一样,也需要# define保扩。 5.函数参数顺序( Function Parameter Ordering) 定义函数时,参数顺序为:输入参数在前,输出参数在后 C/C++函数参数分为输入参数和输出参数两种,有时输入参数也会输岀(译者注:佰被修 改时)。输入参数一般传值或常数引用( const references),输出参数或输入/输出参数 为非常数指针(non- const pointers)。对参数排序时,将所有输入参数置」输出参数之 前。不要仅仅因为是新添加的参数,就将其置于最后,而应该依然置于输出参数之前。 这点并不是必须遵循的规则,输入/输山两用参数(通常是类/结枃体变量)混在其中,会 使得规则难以遵循。 5.包含文件的名称及次序 将包含次序标准化可增强可读性、避免隐藏依赖( hidden dependencies,译者注:隐藏 依赖主要是指包含的文件中编译时),次序如下:C库、C++库、其他库的.h、项目內的.h。 项目内头文件应按照项目源代码目录树结构排列,并且避免使用UNX文件路径.(当前目 录)和,(父日录)。例如, google- awesome- project,/src/base/ logging. h应像这样 被包含: #include base/logging. h dir/foo.cc的上要作用是执行或测试dir2/foo2h的功能,foo.CC中包含头文件的次序如 dir2/foo2.h(优先位置,详情如下) C系统文件 C++系统文件 其他库头文件 本项目内头文件 这种排序方式可有效减少隐臧依赖,我们希望每一个头文件独立编译。最简单的实现方式是 将其作为第一个.h文件包含在对应的.CC中。 dir/foo.cc和dir2/foo2.h通常位于相冋目录下(像base/ basictypes_ unittest.Cc和 base/ basictypes h),但也可在不同目录下。 相同目录下头文件按宇母序是不锆的选择。 举例来说, google- awesome- project/src/foo/ interna/ fooserver.CC的包含次序如下 # include"foo/ public/ fooserver h"//优先位置 # include <sys/types. h> #include <unistd.h> #include shash map> *include <vector> # include base/basicty pes h #include base/ commandlineflags h #include foo/publicbar. h 作用域 1。命名空间( Namespaces) 在,CC文件中,提倡使用不具名的命名空间( unnamed namespaces,译者注:不具名的 命名空间就像不具名的类一样,似乎被介绍的很少:-()。使用具名命名空间时,其名称可 基于项目或路径名称,不要使用 using指示符。 定义:命名空间将全局作用域细分为不同的、具名的作用域,可有效防止全局作用域的命名 冲突。 优点:命名空间提供了(可嵌套)命名轴线( name axis,详者注:将命名分割在不同命 名空间内),当然,类也提供了(可嵌套)的命名轴线(译者注:将命名分割在不同类的作 用域内 举例来说,两个不同项目的全局作用域都有一个类Foo,这样在编译或运行时造成冲突。如 果每个项目将代码置于不同命名空间中, project1::Foo和 project.2:Foo作为不同符号 自然不会冲突。 缺点:命名空间具有迷惑性,因为它们和类一样提供了额外的(可嵌套的)命名轴线。在头 文件中使用不具名的空间容易违背C++的唯一定义原则( One definition rule(ODR) 结论:根据下文将要提到的策略合珥使用命名空间 1)不具名命名空间( Unnamed Namespaces) 在.CC文件中,允许甚至提倡使用不具名命名空间,以避免运行时的命名冲突 namespace i //.cc文件中 //命名空间的内容无需缩进 enum UNUSED, EOF, ERROR 3; /经常使用的符号 boo| AtEof(){ return pos==EOF;}//使用本命名空间内的符号EOF / namespace 然而,与特定类关联的文件作用域声明在该类中被声明为类型、静态数据成员或静态成员函 数,而不是不只名命名空间的成员。像上文展小的那样,不只名命名空间结束时用注释/ namespace标识 不能在.h文件中使用不具名命名空间 2)具名命名空间( Named Namespaces) 具名命名空间使用方式如下: 命名空间将除文件包含、全局标识的声明/定义以及类的前置声明外的整个源文件封装起来, 以同其他命名空间相区分。 //.h文件 namespace mynamespace i /所有声明都置于命名空间中 ∥/注意不要使用缩进 class My class i public void Food 3 / namespace mynamespace /.cc文件 namespace mynamespace i /函数定义都置于命名空间中 void MyClass: Foot s / namespace mynamespace 通常的CC文件会包含更多、更复杂的细节,包括对其他命名空间中类的引用等。 #include ah DEFINE_bool(someflag, false, dummy flag") class o;//全局命名空间中类C的前置声明 namespace a{ class a;}∥/命名空间a中的类a:A的前置声明 namespace bt code for b /b中的代码 1// namespace b 不要声明命名空间std下的任何内容,包括标准库类的前置声明。声明std下的实体会导 致不明确的行为,如,不可移植。声明标准斥下的实体,需要包含对应的头文件。 最好不要使用 using指示符,以保证命名空间下的所有名称都可以正常使用。 ∥/禁止一一污柒命名空间 using namespace toO; 在.CC文件、h文件的函数、方法或类中,可以使用 using。 //允许:C文件中 /.h文件中,必须在函数、方法或类的内部使用 using foo: bar: 在.CC文件、h文件的函数、方法或类中,还可以使用命名空间别名。 /允许:.CC文件中 /.h文件中,必须在函数、方法或类的内部使用 namespace fbz=: foo: bar: baz; 2.嵌套类( Nested class) 当公丌嵌套类作为接∏的部分时,虽然可以直接将他们保持在全局作用域中,但将嵌套类 的声明置于命名空间中是更好的选择。 定义:可以在一个类中定义另一个类,嵌套类也称成员类( member class)。 class Foo t private /Bar是嵌套在Foo中的成员类 class Bar i 优点:当嵌套(成员)类只在被嵌套类( enclosing class)中使用时很有用,将其置于被 嵌套类作用域作为被嵌套类的成员不会污染其他作用域同名类。可在被嵌套类中前置声明嵌 套类,在.CC文件中定义嵌套类,避免在被嵌套类中包含嵌套类的定义,因为嵌套类的定义 通常只与实现相关 缺点:只能在被嵌套类的定义中才能前置声明嵌套类。因此,任何使用Foo:Bar*指针的 头文件必须包含整个Foo的声明。 结论:不要将嵌套类定义为 public,除非它们是接口的一部分,比如,某个方法使用了这 个类的一系列选项。 3.非成员函数( Nonmember)、静态成员函数( static Member)和全局函数( doba Functions) 使用命名空间中的非成员函数或静态成员函数,尽量不要使用全局函数。 优点:某些情况下,非成员函数和静态成员函数是非常有用的,将非成员函数置于命名空间 中可避免对全局作用域的污染。 缺点:将非成员函数和静态成员函数作为新类的成员或许更有意义,当它们需要访问外部资 源或具有重要依赖时更是如此。 结论 有时,不把函数限定在类的实体中是有益的,甚至需要这么做,要么作为静态成员,要么作 为非成员函数。非成员函数不应依赖于外部变量,并尽量置于某个命名空间中。相比单纯为 了封装若干不共享任何静态数据的静态成员函数而创建类,不如使用命名空间 定义于同一编译单元的函数,被其他编译单元直接调用可能会引入不必要的耦合和迕接依 赖;静态成员函数对此尤其敏感。可以考虑提取到新类中,或者将函数置于独立库的命名空 间中。 如果你确实需要定义非成员函数,又只是在.CC文件中使用它,可使用不具名命名空间或 static关联(如 static int foo(..})限定其作用域。 4.局部变量( Local variab|es) 将函数变量尽可能置于最小作用域内,在声明变量时将其初始化。 C++允许在函数的任何位置声明变量。我们提倡在尽可能小的作用域中声明变量,离第 次使用越近越好。这使得代码易于阅读,易于定位变量的声眀位置、变量类型和初始值。特 别是,应使用初始化代替声明+赋值的方式 int i. i=f();//坏一一初始化和声明分离 ntj=g0;∥/好一一初始化时声明 注意:gcc可正确执行for(inti=0;i<10;++i)(i的作用域仅限for循环),因此其 他for循环中可重用i。诉和 While等语句中,作用域声明( scope declaration)同样是 正确的。 while(const charx p= strchr(str,,))str=p+1 注意:如果变量是一个对象,每次进入作用域都要调用其构造函数,每次退出作用域都要调 用其析构函数。 /低效的实现 for(inti=0;i<1000000;++){ Foof;//构造函数和析构函数分别调用1000000次! f Dosomething (]: 类似变量放到循环作用域外面声明要晑效的多 Foof;∥/构造函数和析构函数只调用1次 for(inti=0;i<1000000;++i){ f Dosomething(i) 5。全局变量( Global variables) cass类型的全局变量是被禁止的,内建类型的全局变量是允许的,当然多线稈代码中非常 数全局变量也是被禁止的。永远不要使用函数返回值初始化全局变量 不幸的是,全局变量的构造函数、析构函数以及初始化操作的调用顺序只是被部分规定,每 次生成有可能会有变化,从而导致难以发现的bugs 因此,禁止使用 class类型的全局变量(包括STL的 string,Ⅴ ector等等),因为它们的 初始化顺序有可能导致构造出现问题。内建类型和由内建类型构成的没有构造睬数的结构体

...展开详情
试读 53P Google C++ 编程规范
立即下载 低至0.43元/次 身份认证VIP会员低至7折
    一个资源只可评论一次,评论内容不能少于5个字
    pinger00 目前看,跟google网页上的有些差异
    2019-11-14
    回复
    • 分享宗师

      成功上传21个资源即可获取
    关注 私信 TA的资源
    上传资源赚积分,得勋章
    最新推荐
    Google C++ 编程规范 9积分/C币 立即下载
    1/53
    Google C++ 编程规范第1页
    Google C++ 编程规范第2页
    Google C++ 编程规范第3页
    Google C++ 编程规范第4页
    Google C++ 编程规范第5页
    Google C++ 编程规范第6页
    Google C++ 编程规范第7页
    Google C++ 编程规范第8页
    Google C++ 编程规范第9页
    Google C++ 编程规范第10页
    Google C++ 编程规范第11页
    Google C++ 编程规范第12页
    Google C++ 编程规范第13页
    Google C++ 编程规范第14页
    Google C++ 编程规范第15页
    Google C++ 编程规范第16页

    试读已结束,剩余37页未读...

    9积分/C币 立即下载 >