ZAGoogle计算机编程规范完整029Word文档格式.docx

上传人:b****2 文档编号:1256245 上传时间:2023-04-30 格式:DOCX 页数:69 大小:62.69KB
下载 相关 举报
ZAGoogle计算机编程规范完整029Word文档格式.docx_第1页
第1页 / 共69页
ZAGoogle计算机编程规范完整029Word文档格式.docx_第2页
第2页 / 共69页
ZAGoogle计算机编程规范完整029Word文档格式.docx_第3页
第3页 / 共69页
ZAGoogle计算机编程规范完整029Word文档格式.docx_第4页
第4页 / 共69页
ZAGoogle计算机编程规范完整029Word文档格式.docx_第5页
第5页 / 共69页
ZAGoogle计算机编程规范完整029Word文档格式.docx_第6页
第6页 / 共69页
ZAGoogle计算机编程规范完整029Word文档格式.docx_第7页
第7页 / 共69页
ZAGoogle计算机编程规范完整029Word文档格式.docx_第8页
第8页 / 共69页
ZAGoogle计算机编程规范完整029Word文档格式.docx_第9页
第9页 / 共69页
ZAGoogle计算机编程规范完整029Word文档格式.docx_第10页
第10页 / 共69页
ZAGoogle计算机编程规范完整029Word文档格式.docx_第11页
第11页 / 共69页
ZAGoogle计算机编程规范完整029Word文档格式.docx_第12页
第12页 / 共69页
ZAGoogle计算机编程规范完整029Word文档格式.docx_第13页
第13页 / 共69页
ZAGoogle计算机编程规范完整029Word文档格式.docx_第14页
第14页 / 共69页
ZAGoogle计算机编程规范完整029Word文档格式.docx_第15页
第15页 / 共69页
ZAGoogle计算机编程规范完整029Word文档格式.docx_第16页
第16页 / 共69页
ZAGoogle计算机编程规范完整029Word文档格式.docx_第17页
第17页 / 共69页
ZAGoogle计算机编程规范完整029Word文档格式.docx_第18页
第18页 / 共69页
ZAGoogle计算机编程规范完整029Word文档格式.docx_第19页
第19页 / 共69页
ZAGoogle计算机编程规范完整029Word文档格式.docx_第20页
第20页 / 共69页
亲,该文档总共69页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

ZAGoogle计算机编程规范完整029Word文档格式.docx

《ZAGoogle计算机编程规范完整029Word文档格式.docx》由会员分享,可在线阅读,更多相关《ZAGoogle计算机编程规范完整029Word文档格式.docx(69页珍藏版)》请在冰点文库上搜索。

ZAGoogle计算机编程规范完整029Word文档格式.docx

2.头文件依赖

使用前置声明(forwarddeclarations)尽量减少.h文件中#include的数量。

当一个头文件被包含的同时也引入了一项新的依赖(dependency),只要该头文件被修改,代码就要重新编译。

如果你的头文件包含了其他头文件,这些头文件的任何改变也将导致那些包含了你的头文件的代码重新编译。

因此,我们宁可尽量少包含头文件,尤其是那些包含在其他头文件中的。

使用前置声明可以显著减少需要包含的头文件数量。

举例说明:

头文件中用到类File,但不需要访问File的声明,则头文件中只需前置声明classFile;

无需#include"

file/base/file.h"

在头文件如何做到使用类Foo而无需访问类的定义?

1)将数据成员类型声明为Foo*或Foo&

2)参数、返回值类型为Foo的函数只是声明(但不定义实现)。

3)静态数据成员的类型可以被声明为Foo,因为静态数据成员的定义在类定义之外。

另一方面,如果你的类是Foo的子类,或者含有类型为Foo的非静态数据成员,则必须为之包含头文件。

有时,使用指针成员(pointermembers,如果是scoped_ptr更好)替代对象成员(objectmembers)的确更有意义。

然而,这样的做法会降低代码可读性及执行效率。

如果仅仅为了少包含头文件,还是不要这样替代的好。

当然,.cc文件无论如何都需要所使用类的定义部分,自然也就会包含若干头文件。

译者注:

能依赖声明的就不要依赖定义。

3.内联函数

只有当函数只有10行甚至更少时才会将其定义为内联函数(inlinefunction)。

定义(Definition):

当函数被声明为内联函数之后,编译器可能会将其内联展开,无需按通常的函数调用机制调用内联函数。

优点:

当函数体比较小的时候,内联该函数可以令目标代码更加高效。

对于存取函数(accessor、mutator)以及其他一些比较短的关键执行函数。

缺点:

滥用内联将导致程序变慢,内联有可能是目标代码量或增或减,这取决于被内联的函数的大小。

内联较短小的存取函数通常会减少代码量,但内联一个很大的函数(译者注:

如果编译器允许的话)将戏剧性的增加代码量。

在现代处理器上,由于更好的利用指令缓存(instructioncache),小巧的代码往往执行更快。

结论:

一个比较得当的处理规则是,不要内联超过10行的函数。

对于析构函数应慎重对待,析构函数往往比其表面看起来要长,因为有一些隐式成员和基类析构函数(如果有的话)被调用!

另一有用的处理规则:

内联那些包含循环或switch语句的函数是得不偿失的,除非在大多数情况下,这些循环或switch语句从不执行。

重要的是,虚函数和递归函数即使被声明为内联的也不一定就是内联函数。

通常,递归函数不应该被声明为内联的(译者注:

递归调用堆栈的展开并不像循环那么简单,比如递归层数在编译时可能是未知的,大多数编译器都不支持内联递归函数)。

析构函数内联的主要原因是其定义在类的定义中,为了方便抑或是对其行为给出文档。

4.-inl.h文件

复杂的内联函数的定义,应放在后缀名为-inl.h的头文件中。

在头文件中给出内联函数的定义,可令编译器将其在调用处内联展开。

然而,实现代码应完全放到.cc文件中,我们不希望.h文件中出现太多实现代码,除非这样做在可读性和效率上有明显优势。

如果内联函数的定义比较短小、逻辑比较简单,其实现代码可以放在.h文件中。

例如,存取函数的实现理所当然都放在类定义中。

出于实现和调用的方便,较复杂的内联函数也可以放到.h文件中,如果你觉得这样会使头文件显得笨重,还可以将其分离到单独的-inl.h中。

这样即把实现和类定义分离开来,当需要时包含实现所在的-inl.h即可。

-inl.h文件还可用于函数模板的定义,从而使得模板定义可读性增强。

要提醒的一点是,-inl.h和其他头文件一样,也需要#define保护。

5.函数参数顺序(FunctionParameterOrdering)

定义函数时,参数顺序为:

输入参数在前,输出参数在后。

C/C++函数参数分为输入参数和输出参数两种,有时输入参数也会输出(译者注:

值被修改时)。

输入参数一般传值或常数引用(constreferences),输出参数或输入/输出参数为非常数指针(non-constpointers)。

对参数排序时,将所有输入参数置于输出参数之前。

不要仅仅因为是新添加的参数,就将其置于最后,而应该依然置于输出参数之前。

这一点并不是必须遵循的规则,输入/输出两用参数(通常是类/结构体变量)混在其中,会使得规则难以遵循。

6.包含文件的名称及秩序

将包含秩序标准化可增强可读性、避免隐藏依赖(hiddendependencies,译者注:

隐藏依赖主要是指包含的文件中编译时),秩序如下:

C库、C++库、其他库的.h、项目内的.h。

项目内头文件应按照项目源代码目录树结构排列,并且避免使用UNIX文件路径.(当前目录)和..(父目录)。

例如,google-awesome-project/src/base/logging.h应像这样被包含:

#include"

base/logging.h"

dir/foo.cc的主要作用是执行或测试dir2/foo2.h的功能,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/internal/fooserver.cc的包含秩序如下:

foo/public/fooserver.h"

//优先位置

#include<

sys/types.h>

unistd.h>

hash_map>

vector>

base/basictypes.h"

base/commandlineflags.h"

foo/public/bar.h"

______________________________________

译者:

英语不太好,翻译的也就不太好。

这一篇主要提到的是头文件的一些规则,汇总报告一下:

1.避免多重包含是学编程时最基本的要求。

2.前置声明是为了降低编译依赖,防止修改一个头文件引发多米诺效应。

3.内联函数的合理使用可提高代码执行效率。

4.-inl.h可提高代码可读性(一般用不到吧:

D)。

5.标准化函数参数顺序可以提高可读性和易维护性(对函数参数的堆栈空间有轻微影响,我以前大多是相同类型放在一起)。

6.包含文件的名称使用.和..虽然方便却易混乱,使用比较完整的项目路径看上去很清晰、很条理,包含文件的秩序除了美观之外,最重要的是可以减少隐藏依赖,使每个头文件在“最需要编译”(对应源文件处:

D)的地方编译,有人提出库文件放在最后,这样出错先是项目内的文件,头文件都放在对应源文件的最前面,这一点足以保证内部不对的及时发现了。

∙作用域

1.命名空间(Namespaces)

在.cc文件中,提倡使用不具名的命名空间(unnamednamespaces,译者注:

不具名的命名空间就像不具名的类一样,似乎被推荐的很少:

-()。

使用具名命名空间时,其名称可基于项目或路径名称,不要使用using指示符。

定义:

命名空间将全局作用域细分为不同的、具名的作用域,可有效防止全局作用域的命名冲突。

命名空间提供了(可嵌套)命名轴线(nameaxis,译者注:

将命名分割在不同命名空间内),当然,类也提供了(可嵌套)的命名轴线(译者注:

将命名分割在不同类的作用域内)。

举例来说,两个不同项追求全局作用域都有一个类Foo,这样在编译或运行时造成冲突。

如果每个项目将代码置于不同命名空间中,project1:

:

Foo和project2:

Foo作为不同符号自然不会冲突。

命名空间具有迷惑性,因为它们和类一样提供了额外的(可嵌套的)命名轴线。

在头文件中使用不具名的空间容易违背C++的唯一定义原则(OneDefinitionRule(ODR))。

根据下文将要提到的策略合理使用命名空间。

1)不具名命名空间(UnnamedNamespaces)

在.cc文件中,允许甚至提倡使用不具名命名空间,以避免运行时的命名冲突:

namespace{//.cc文件中

//命名空间的内容无需缩进

enum{UNUSED,EOF,ERROR};

//经常使用的符号

boolAtEof(){returnpos_==EOF;

}//使用本命名空间内的符号EOF

}//namespace

然而,与特定类关联的文件作用域声明在该类中被声明为类型、静态数据成员或静态成员函数,而不是不具名命名空间的成员。

像上文展示的那样,不具名命名空间结束时用注释//namespace标识。

不能在.h文件中使用不具名命名空间。

2)具名命名空间(NamedNamespaces)

具名命名空间使用方式如下:

命名空间将除文件包含、全局标识的声明/定义以及类的前置声明外的整个源文件封装起来,以同其他命名空间相区分。

//.h文件

namespacemynamespace{

//所有声明都置于命名空间中

//注意不要使用缩进

classMyClass{

public:

...

voidFoo();

};

}//namespacemynamespace

//.cc文件

//函数定义都置于命名空间中

voidMyClass:

Foo(){

}

通常的.cc文件会包含更多、更复杂的细节,包括对其他命名空间中类的引用等。

a.h"

DEFINE_bool(someflag,false,"

dummyflag"

);

classC;

//全局命名空间中类C的前置声明

namespacea{classA;

}//命名空间a中的类a:

A的前置声明

namespaceb{

...codeforb...//b中的代码

}//namespaceb

不要声明命名空间std下的任何内容,包括标准库类的前置声明。

声明std下的实思想到导致不明确的行为,如,不可移植。

声明标准库下的实体,需要包含对应的头文件。

最好不要使用using指示符,以保证命名空间下的所有名称都可以正常使用。

//禁止——污染命名空间

usingnamespacefoo;

在.cc文件、.h文件的函数、方法或类中,可以使用using。

//允许:

.cc文件中

//.h文件中,必须在函数、方法或类的内部使用

using:

foo:

bar;

在.cc文件、.h文件的函数、方法或类中,还可以使用命名空间别名。

namespacefbz=:

bar:

baz;

2.嵌套类(NestedClass)

当公开嵌套类作为接口的一部分时,虽然可以直接将他们保持在全局作用域中,但将嵌套类的声明置于命名空间中是更好的选择。

可以在一个类中定义另一个类,嵌套类也称成员类(memberclass)。

classFoo{

private:

//Bar是嵌套在Foo中的成员类

classBar{

};

当嵌套(成员)类只在被嵌套类(enclosingclass)中使用时很有用,将其置于被嵌套类作用域作为被嵌套类的成员不会污染其他作用域同名类。

可在被嵌套类中前置声明嵌套类,在.cc文件中定义嵌套类,避免在被嵌套类中包含嵌套类的定义,因为嵌套类的定义通常只与实现相关。

只能在被嵌套类的定义中才能前置声明嵌套类。

因此,任何使用Foo:

Bar*指针的头文件必须包含整个Foo的声明。

不要将嵌套类定义为public,除非它们是接口的一部分,比如,某个方法使用了这个类的一系列选项。

3.非成员函数(Nonmember)、静态成员函数(StaticMember)和全局函数(GlobalFunctions)

使用命名空间中的非成员函数或静态成员函数,尽量不要使用全局函数。

某些情况下,非成员函数和静态成员函数是非常有用的,将非成员函数置于命名空间中可避免对全局作用域的污染。

将非成员函数和静态成员函数作为新类的成员或许更有意义,当它们需要访问外部资源或具有重要依赖时更是如此。

有时,不把函数限定在类的实体中是有益的,甚至需要这么做,要么作为静态成员,要么作为非成员函数。

非成员函数不应依赖于外部变量,并尽量置于某个命名空间中。

相比单纯为了封装若干不共享任何静态数据的静态成员函数而创建类,不如使用命名空间。

定义于同一编译单元的函数,被其他编译单元直接调用可能会引入不必要的耦合和连接依赖。

静态成员函数对此尤其敏感。

可以考虑提取到新类中,或者将函数置于独立库的命名空间中。

如果你确实需要定义非成员函数,又只是在.cc文件中使用它,可使用不具名命名空间或static关联(如staticintFoo(){...})限定其作用域。

4.局部变量(LocalVariables)

将函数变量尽可能置于最小作用域内,在声明变量时将其初始化。

C++允许在函数的任何位置声明变量。

我们提倡在尽可能小的作用域中声明变量,离第一次使用越近越好。

这使得代码易于阅读,易于定位变量的声明位置、变量类型和初始值。

特别是,应使用初始化代替声明+赋值的方式。

inti;

i=f();

//坏——初始化和声明分离

ntj=g();

//好——初始化时声明

gcc可正确执行for(inti=0;

i<

10;

++i)(i的作用域仅限for循环),因此其他for循环中可重用i。

if和while等语句中,作用域声明(scopedeclaration)同样是正确的。

while(constchar*p=strchr(str,'

/'

))str=p+1;

如果变量是一个对象,每次进入作用域都要调用其构造函数,每次退出作用域都要调用其析构函数。

//低效的实现

for(inti=0;

1000000;

++i){

Foof;

//构造函数和析构函数分别调用1000000次!

f.DoSomething(i);

类似变量放到循环作用域外面声明要高效的多:

Foof;

//构造函数和析构函数只调用1次

5.全局变量(GlobalVariables)

class类型的全局变量是被禁止的,内建类型的全局变量是允许的,当然多线程代码中非常数全局变量也是被禁止的。

永远不要使用函数返回值初始化全局变量。

不幸的是,全局变量的构造函数、析构函数以及初始化制作的调用顺序只是被部分要求,每次生成有可能会有变化,从而导致难以发现的bugs。

因此,禁止使用class类型的全局变量(包括STL的string,vector等等),因为它们的初始化顺序有可能导致构造出现问题。

内建类型和由内建类型构成的没有构造函数的结构体可以使用,如果你一定要使用class类型的全局变量,请使用单件模式(singletonpattern)。

对于全局的字符串常量,使用C风格的字符串,而不要使用STL的字符串:

constcharkFrogSays[]="

ribbet"

;

虽然允许在全局作用域中使用全局变量,使用时务必三思。

大多数全局变量应该是类的静态数据成员,或者当其只在.cc文件中使用时,将其定义到不具名命名空间中,或者使用静态关联以限制变量的作用域。

记住,静态成员变量视作全局变量,所以,也不能是class类型!

这一篇主要提到的是作用域的一些规则,汇总报告一下:

1..cc中的不具名命名空间可避免命名冲突、限定作用域,避免直接使用using提示符污染命名空间。

2.嵌套类符合局部使用原则,只是不能在其他头文件中前置声明,尽量不要public。

3.尽量不用全局函数和全局变量,考虑作用域和命名空间限制,尽量单独形成编译单元。

4.多线程中的全局变量(含静态成员变量)不要使用class类型(含STL容器),避免不明确行为导致的bugs。

作用域的使用,除了考虑名称污染、可读性之外,主要是为降低耦合度,提高编译、执行效率。

∙类

类是C++中基本的代码单元,自然被广泛使用。

本节列举了在写一个类时要做什么、不要做什么。

1.构造函数(Constructor)的职责

构造函数中只进行那些没有实际意义的(trivial,译者注:

简单初始化对于程序执行没有实际的逻辑意义,因为成员变量的“有意义”的值大多不在构造函数中确定)初始化,可能的话,使用Init()方法集中初始化为有意义的(non-trivial)数据。

在构造函数中执行初始化制作。

排版方便,无需担心类是否初始化。

在构造函数中执行制作引起的问题有:

1)构造函数中不易报告不对,不能使用异常。

2)制作失败会造成对象初始化失败,引起不确定状态。

3)构造函数内调用虚函数,调用不会派发到子类实现中,即使当前没有子类化实现,将来仍是隐患。

4)如果有人创建该类型的全局变量(虽然违背了上节提到的规则),构造函数将在main()之前被调用,有可能破坏构造函数中暗含的假设条件。

例如,gflags尚未初始化。

如果对象需要有意义的(non-trivial)初始化,考虑使用另外的Init()方法并(或)增加一个成员标记用于指示对象是否已经初始化成功。

2.默认构造函数(DefaultConstructors)

如果一个类定义了若干成员变量又没有其他构造函数,需要定义一个默认构造函数,否则编译器将自动生产默认构造函数。

新建一个没有参数的对象时,默认构造函数被调用,当调用new[](为数组)时,默认构造函数总是被调用。

默认将结构体初始化为“不可能的”值,使调试更加容易。

对代码编写者来说,这是多余的工作。

如果类中定义了成员变量,没有提供其他构造函数,你需要定义一个默认构造函数(没有参数)。

默认构造函数更适合于初始化对象,使对象内部状态(internalstate)一致、有效。

提供默认构造函数的原因是:

如果你没有提供其他构造函数,又没有定义默认构造函数,编译器将为你自动生成一个,编译器生成的构造函数并不会对对象进行初始化。

如果你定义的类继承现有类,而你又没有增加新的成员变量,则不需要为新类定义默认构造函数。

3.明确的构造函数(ExplicitConstructors)

对单参数构造函数使用C++关键字explicit。

通常,只有一个参数的构造函数可被用于转换(conversion,译者注:

主要指隐式转换,下文可见),例如,定义了Foo:

Foo(stringname),当向需要传入一个Foo对象的函数传入一个字符串时,构造函数Foo:

Foo(stringname)被调用并将该字符串转换为一个Foo临时对象传给调用函数。

看上去很方便,但如果你并不希望如此通过转换生成一个新对象的话,麻烦也随之而来。

为避免构造函数被调用造成隐式转换,可以将其声明为explicit。

避免不合时宜的变换。

无。

所有单参数构造函数必须是明确的。

在类定义中,将关键字explicit加到单参数构造函数前:

explicitFoo(stringname);

例外:

在少数情况下,拷贝构造函数可以不声明为explicit。

特意作为其他类的透明包装器的类。

类似例外情况应在注释中明确说明。

4.拷贝构造函数(CopyConstructors)

仅在代码中需要拷贝一个类对象的时候使用拷贝构造函数。

不需要拷贝时应使用DISALLOW_COPY_AND_ASSIGN。

通过拷贝新建对象时可使用拷贝构造函数(特别是对象的传值时)。

拷贝构造函数使得拷贝对象更加容易,STL容器要求所有内容可拷贝、可赋值。

C++中对象的隐式拷贝是导致很多性能问题和bugs的根源。

拷贝构造函数降低了代码可读性,相比按引用传

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

当前位置:首页 > 小学教育 > 语文

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2