收藏 分享(赏)

Google-C++编程规范(完整).doc

上传人:weiwoduzun 文档编号:2891175 上传时间:2018-09-29 格式:DOC 页数:63 大小:186.50KB
下载 相关 举报
Google-C++编程规范(完整).doc_第1页
第1页 / 共63页
Google-C++编程规范(完整).doc_第2页
第2页 / 共63页
Google-C++编程规范(完整).doc_第3页
第3页 / 共63页
Google-C++编程规范(完整).doc_第4页
第4页 / 共63页
Google-C++编程规范(完整).doc_第5页
第5页 / 共63页
点击查看更多>>
资源描述

1、Google C+编程规范1 背景Google的开源项目大多使用C+开发。每一个C+程序员也都知道,C+具有很多强大的语言特性,但这种强大不可避免的导致它的复杂,这种复杂会使得代码更易于出现bug、难于阅读和维护。本指南的目的是通过详细阐述在C+编码时要怎样写、不要怎样写来规避其复杂性。这些规则可在允许代码有效使用C+语言特性的同时使其易于管理。风格,也被视为可读性,主要指称管理C+代码的习惯。使用术语风格有点用词不当,因为这些习惯远不止源代码文件格式这么简单。使代码易于管理的方法之一是增强代码一致性,让别人可以读懂你的代码是很重要的,保持统一编程风格意味着可以轻松根据“模式匹配”规则推断各种

2、符号的含义。创建通用的、必需的习惯用语和模式可以使代码更加容易理解,在某些情况下改变一些编程风格可能会是好的选择,但我们还是应该遵循一致性原则,尽量不这样去做。本指南的另一个观点是C+特性的臃肿。C+是一门包含大量高级特性的巨型语言,某些情况下,我们会限制甚至禁止使用某些特性使代码简化,避免可能导致的各种问题,指南中列举了这类特性,并解释说为什么这些特性是被限制使用的。由Google开发的开源项目将遵照本指南约定。注意:本指南并非C+教程,我们假定读者已经对C+非常熟悉。 头文件通常,每一个.cc文件(C+的源文件)都有一个对应的.h文件(头文件),也有一些例外,如单元测试代码和只包含main

3、()的.cc文件。正确使用头文件可令代码在可读性、文件大小和性能上大为改观。下面的规则将引导你规避使用头文件时的各种麻烦。1. #define的保护所有头文件都应该使用#define防止头文件被多重包含(multiple inclusion),命名格式当是: _H_为保证唯一性,头文件的命名应基于其所在项目源代码树的全路径。例如,项目foo中的头文件foo/src/bar/baz.h按如下方式保护:#ifndef FOO_BAR_BAZ_H_#define FOO_BAR_BAZ_H_.#endif / FOO_BAR_BAZ_H_Google C+编程规范22. 头文件依赖使用前置声明(fo

4、rward declarations)尽量减少.h文件中#include的数量。当一个头文件被包含的同时也引入了一项新的依赖(dependency),只要该头文件被修改,代码就要重新编译。如果你的头文件包含了其他头文件,这些头文件的任何改变也将导致那些包含了你的头文件的代码重新编译。因此,我们宁可尽量少包含头文件,尤其是那些包含在其他头文件中的。使用前置声明可以显著减少需要包含的头文件数量。举例说明:头文件中用到类File ,但不需要访问File的声明,则头文件中只需前置声明class File;无需#include “file/base/file.h“。在头文件如何做到使用类Foo而无需访问

5、类的定义?1) 将数据成员类型声明为Foo *或Foo / 经常使用的符号Google C+编程规范6bool AtEof() return pos_ = EOF; / 使用本命名空间内的符号EOF / namespace然而,与特定类关联的文件作用域声明在该类中被声明为类型、静态数据成员或静态成员函数,而不是不具名命名空间的成员。像上文展示的那样,不具名命名空间结束时用注释/ namespace标识。不能在.h文件中使用不具名命名空间。2) 具名命名空间(Named Namespaces)具名命名空间使用方式如下:命名空间将除文件包含、全局标识的声明/定义以及类的前置声明外的整个源文件封装起

6、来,以同其他命名空间相区分。/ .h文件namespace mynamespace / 所有声明都置于命名空间中/ 注意不要使用缩进class MyClass public:.void Foo(); / namespace mynamespace/ .cc文件namespace mynamespace / 函数定义都置于命名空间中void MyClass:Foo() . / namespace mynamespace通常的.cc文件会包含更多、更复杂的细节,包括对其他命名空间中类的引用等。#include “a.h“DEFINE_bool(someflag, false, “dummy fla

7、g“);Google C+编程规范7class C; / 全局命名空间中类 C的前置声明namespace a class A; / 命名空间a中的类a:A 的前置声明namespace b .code for b. / b中的代码 / namespace b不要声明命名空间std下的任何内容,包括标准库类的前置声明。声明std 下的实体会导致不明确的行为,如,不可移植。声明标准库下的实体,需要包含对应的头文件。最好不要使用using指示符,以保证命名空间下的所有名称都可以正常使用。/ 禁止 污染命名空间using namespace foo;在.cc文件、.h文件的函数、方法或类中,可以使用

8、using。/ 允许:.cc文件中/ .h文件中,必须在函数、方法或类的内部使用using :foo:bar;在.cc文件、.h文件的函数、方法或类中,还可以使用命名空间别名。/ 允许:.cc文件中/ .h文件中,必须在函数、方法或类的内部使用namespace fbz = :foo:bar:baz;2. 嵌套类(Nested Class)当公开嵌套类作为接口的一部分时,虽然可以直接将他们保持在全局作用域中,但将嵌套类的声明置于命名空间中是更好的选择。定义:可以在一个类中定义另一个类,嵌套类也称成员类(member class)。class Foo private:/ Bar是嵌套在Foo中的

9、成员类class Bar .;Google C+编程规范8;优点:当嵌套(成员)类只在被嵌套类(enclosing class)中使用时很有用,将其置于被嵌套类作用域作为被嵌套类的成员不会污染其他作用域同名类。可在被嵌套类中前置声明嵌套类,在.cc文件中定义嵌套类,避免在被嵌套类中包含嵌套类的定义,因为嵌套类的定义通常只与实现相关。缺点:只能在被嵌套类的定义中才能前置声明嵌套类。因此,任何使用Foo:Bar* 指针的头文件必须包含整个Foo的声明。结论:不要将嵌套类定义为public,除非它们是接口的一部分,比如,某个方法使用了这个类的一系列选项。3. 非成员函数( Nonmember)、静态

10、成员函数(Static Member)和全局函数(Global Functions)使用命名空间中的非成员函数或静态成员函数,尽量不要使用全局函数。优点:某些情况下,非成员函数和静态成员函数是非常有用的,将非成员函数置于命名空间中可避免对全局作用域的污染。缺点:将非成员函数和静态成员函数作为新类的成员或许更有意义,当它们需要访问外部资源或具有重要依赖时更是如此。结论:有时,不把函数限定在类的实体中是有益的,甚至需要这么做,要么作为静态成员,要么作为非成员函数。非成员函数不应依赖于外部变量,并尽量置于某个命名空间中。相比单纯为了封装若干不共享任何静态数据的静态成员函数而创建类,不如使用命名空间。

11、定义于同一编译单元的函数,被其他编译单元直接调用可能会引入不必要的耦合和连接依赖;静态成员函数对此尤其敏感。可以考虑提取到新类中,或者将函数置于独立库的命名空间中。如果你确实需要定义非成员函数,又只是在.cc文件中使用它,可使用不具名命名空间或 static关联(如static int Foo() .)限定其作用域。4. 局部变量(Local Variables)将函数变量尽可能置于最小作用域内,在声明变量时将其初始化。C+允许在函数的任何位置声明变量。我们提倡在尽可能小的作用域中声明变量,离第一次使用越近越好。这使得代码易于阅读,易于定位变量的声明位置、变量类型和初始值。特别是,应使用初始化

12、代替声明+赋值的方式。int i;Google C+编程规范9i = f(); / 坏 初始化和声明分离nt j = g(); / 好 初始化时声明注意:gcc可正确执行for (int i = 0; i 实现继承接口继承 私有继承,子类重载的虚函数也要声明virtual关键字,虽然编译器允许不这样做;7. 避免使用多重继承,使用时,除一个基类含有实现外,其他基类均为纯接口;8. 接口类类名以Interface 为后缀,除提供带实现的虚析构函数、静态成员函数外,其他均为纯虚函数,不定义非静态数据成员,不提供构造函数,提供的话,声明为protected;9. 为降低复杂性,尽量不重载操作符,模板

13、、标准类中使用时提供文档说明;10. 存取函数一般内联在头文件中;Google C+编程规范1811. 声明次序:public-protected-private;12. 函数体尽量短小、紧凑,功能单一。 Google特有的风情Google有很多自己实现的使C+代码更加健壮的技巧、功能,以及有异于别处的 C+的使用方式。1. 智能指针(Smart Pointers)如果确实需要使用智能指针的话,scoped_ptr完全可以胜任。在非常特殊的情况下,例如对STL容器中对象,你应该只使用 std:tr1:shared_ptr,任何情况下都不要使用 auto_ptr。“智能”指针看上去是指针,其实是

14、附加了语义的对象。以scoped_ptr为例,scoped_ptr 被销毁时,删除了它所指向的对象。shared_ptr也是如此,而且, shared_ptr实现了引用计数(reference-counting),从而只有当它所指向的最后一个对象被销毁时,指针才会被删除。一般来说,我们倾向于设计对象隶属明确的代码,最明确的对象隶属是根本不使用指针,直接将对象作为一个域(field)或局部变量使用。另一种极端是引用计数指针不属于任何对象,这样设计的问题是容易导致循环引用或其他导致对象无法删除的诡异条件,而且在每一次拷贝或赋值时连原子操作都会很慢。虽然不推荐这么做,但有些时候,引用计数指针是最简单

15、有效的解决方案。译者注:看来,Google所谓的不同之处,在于尽量避免使用智能指针:D,使用时也尽量局部化,并且,安全第一。 其他C+特性1. 引用参数(Reference Arguments)所以按引用传递的参数必须加上const。定义:在C语言中,如果函数需要修改变量的值,形参(parameter)必须为指针,如int foo(int *pval)。在C+中,函数还可以声明引用形参:int foo(int 事实上这是一个硬性约定:输入参数为值或常数引用,输出参数为指针;输入参数可以是常数指针,但不能使用非常数引用形参。在强调参数不是拷贝而来,在对象生命期内必须一直存在时可以使用常数指针,最

16、好将这些在注释中详细说明。bind2nd 和mem_fun等STL 适配器不接受引用形参,这种情况下也必须以指针形参声明函数。2. 函数重载(Function Overloading)仅在输入参数类型不同、功能相同时使用重载函数(含构造函数),不要使用函数重载模仿缺省函数参数。定义:可以定义一个函数参数类型为const stringvoid Analyze(const char *text, size_t textlen);优点:通过重载不同参数的同名函数,令代码更加直观,模板化代码需要重载,同时为访问者带来便利。缺点:限制使用重载的一个原因是在特定调用处很难确定到底调用的是哪个函数,另一个原

17、因是当派生类只重载函数的部分变量会令很多人对继承语义产生困惑。此外在阅读库的客户端代码时,因缺省函数参数造成不必要的费解。结论:如果你想重载一个函数,考虑让函数名包含参数信息,例如,使用AppendString() 、AppendInt()而不是Append()。3. 缺省参数(Default Arguments)禁止使用缺省函数参数。优点:经常用到一个函数带有大量缺省值,偶尔会重写一下这些值,缺省参数为很少涉及的例外情况提供了少定义一些函数的方便。缺点:大家经常会通过查看现有代码确定如何使用API,缺省参数使得复制粘贴以前的代码难以呈现所有参数,当缺省参数不适用于新代码时可能导致重大问题。结

18、论:所有参数必须明确指定,强制程序员考虑API和传入的各参数值,避免使用可能不为程序员所知的缺省参数。4. 变长数组和alloca(Variable-Length Arrays and alloca())Google C+编程规范20禁止使用变长数组和alloca()。优点:变长数组具有浑然天成的语法,变长数组和alloca()也都很高效。缺点:变长数组和alloca()不是标准 C+的组成部分,更重要的是,它们在堆栈(stack)上根据数据分配大小可能导致难以发现的内存泄漏:“在我的机器上运行的好好的,到了产品中却莫名其妙的挂掉了”。结论:使用安全的分配器(allocator),如scope

19、d_ptr/scoped_array。5. 友元(Friends)允许合理使用友元类及友元函数。通常将友元定义在同一文件下,避免读者跑到其他文件中查找其对某个类私有成员的使用。经常用到友元的一个地方是将FooBuilder声明为Foo的友元,FooBuilder以便可以正确构造Foo的内部状态,而无需将该状态暴露出来。某些情况下,将一个单元测试用类声明为待测类的友元会很方便。友元延伸了(但没有打破)类的封装界线,当你希望只允许另一个类访问某个成员时,使用友元通常比将其声明为public要好得多。当然,大多数类应该只提供公共成员与其交互。6. 异常(Exceptions)不要使用C+异常。优点:

20、1) 异常允许上层应用决定如何处理在底层嵌套函数中发生的“不可能发生”的失败,不像出错代码的记录那么模糊费解;2) 应用于其他很多现代语言中,引入异常使得C+与Python、Java 及其他与C+ 相近的语言更加兼容;3) 许多C+第三方库使用异常,关闭异常将导致难以与之结合;4) 异常是解决构造函数失败的唯一方案,虽然可以通过工厂函数(factory function)或Init()方法模拟异常,但他们分别需要堆分配或新的“非法”状态;5) 在测试框架(testing framework)中,异常确实很好用。缺点:Google C+编程规范211) 在现有函数中添加throw语句时,必须检查

21、所有调用处,即使它们至少具有基本的异常安全保护,或者程序正常结束,永远不可能捕获该异常。例如:if f() calls g() calls h(),h抛出被f捕获的异常,g就要当心了,避免没有完全清理;2) 通俗一点说,异常会导致程序控制流( control flow)通过查看代码无法确定:函数有可能在不确定的地方返回,从而导致代码管理和调试困难,当然,你可以通过规定何时何地如何使用异常来最小化的降低开销,却给开发人员带来掌握这些规定的负担;3) 异常安全需要RAII和不同编码实践。轻松、正确编写异常安全代码需要大量支撑。允许使用异常;4) 加入异常使二进制执行代码体积变大,增加了编译时长(或

22、许影响不大),还可能增加地址空间压力;5) 异常的实用性可能会刺激开发人员在不恰当的时候抛出异常,或者在不安全的地方从异常中恢复,例如,非法用户输入可能导致抛出异常。如果允许使用异常会使得这样一篇编程风格指南长出很多(译者注,这个理由有点牵强:-()!结论:从表面上看,使用异常利大于弊,尤其是在新项目中,然而,对于现有代码,引入异常会牵连到所有依赖代码。如果允许异常在新项目中使用,在跟以前没有使用异常的代码整合时也是一个麻烦。因为Google现有的大多数C+代码都没有异常处理,引入带有异常处理的新代码相当困难。鉴于Google现有代码不接受异常,在现有代码中使用异常比在新项目中使用的代价多少要

23、大一点,迁移过程会比较慢,也容易出错。我们也不相信异常的有效替代方案,如错误代码、断言等,都是严重负担。我们并不是基于哲学或道德层面反对使用异常,而是在实践的基础上。因为我们希望使用Google上的开源项目,但项目中使用异常会为此带来不便,因为我们也建议不要在Google上的开源项目中使用异常,如果我们需要把这些项目推倒重来显然不太现实。对于Windows代码来说,这一点有个例外(等到最后一篇吧:D)。译者注:对于异常处理,显然不是短短几句话能够说清楚的,以构造函数为例,很多C+书籍上都提到当构造失败时只有异常可以处理,Google禁止使用异常这一点,仅仅是为了自身的方便,说大了,无非是基于软

24、件管理成本上,实际使用中还是自己决定。7. 运行时类型识别(Run-Time Type Information, RTTI )Google C+编程规范22我们禁止使用RTTI。定义:RTTI允许程序员在运行时识别C+类对象的类型。优点:RTTI在某些单元测试中非常有用,如在进行工厂类测试时用于检验一个新建对象是否为期望的动态类型。除测试外,极少用到。缺点:运行时识别类型意味著设计本身有问题,如果你需要在运行期间确定一个对象的类型,这通常说明你需要重新考虑你的类的设计。结论:除单元测试外,不要使用RTTI,如果你发现需要所写代码因对象类型不同而动作各异的话,考虑换一种方式识别对象类型。虚函数可

25、以实现随子类类型不同而执行不同代码,工作都是交给对象本身去完成。如果工作在对象之外的代码中完成,考虑双重分发方案,如Visitor模式,可以方便的在对象本身之外确定类的类型。如果你认为上面的方法你掌握不了,可以使用RTTI,但务必请三思,不要去手工实现一个貌似RTTI的方案(RTTI-like workaround),我们反对使用RTTI,同样反对贴上类型标签的貌似类继承的替代方案(译者注,使用就使用吧,不使用也不要造轮子:D)。8. 类型转换(Casting)使用static_casthostname.firstbar()-hostname.second bar()-hostname.fir

26、st, foo-bar()-hostname.second,strerror(errno);你可能会说,“把流封装一下就会比较好了”,这儿可以,其他地方呢?而且不要忘了,我们的目标是使语言尽可能小,而不是添加一些别人需要学习的新的内容。每一种方式都是各有利弊,“没有最好,只有更好”,简单化的教条告诫我们必须从中选择其一,最后的多数决定是printf + read/write。10. 前置自增和自减(Preincrement and Predecrement)对于迭代器和其他模板对象使用前缀形式(+i)的自增、自减运算符。定义:对于变量在自增(+i或i+)或自减(-i或i-)后表达式的值又没有没

27、用到的情况下,需要确定到底是使用前置还是后置的自增自减。优点:不考虑返回值的话,前置自增(+i)通常要比后置自增(-i)效率更高,因为后置的自增自减需要对表达式的值i进行一次拷贝,如果i是迭代器或其他非数值类型,拷贝的代价是比较大的。既然两种自增方式动作一样(译者注,不考虑表达式的值,相信你知道我在说什么),为什么不直接使用前置自增呢?缺点:C语言中,当表达式的值没有使用时,传统的做法是使用后置自增,特别是在for循环中,有些人觉得后置自增更加易懂,因为这很像自然语言,主语(i)在谓语动词(+)前。结论:对简单数值(非对象)来说,两种都无所谓,对迭代器和模板类型来说,要使用前置自增(自减)。1

28、1. const的使用(Use of const)我们强烈建议你在任何可以使用的情况下都要使用const。Google C+编程规范25定义:在声明的变量或参数前加上关键字const用于指明变量值不可修改(如const int foo),为类中的函数加上const限定表明该函数不会修改类成员变量的状态(如class Foo int Bar(char c) const; ;)。优点:人们更容易理解变量是如何使用的,编辑器可以更好地进行类型检测、更好地生成代码。人们对编写正确的代码更加自信,因为他们知道所调用的函数被限定了能或不能修改变量值。即使是在无锁的多线程编程中,人们也知道什么样的函数是安全

29、的。缺点:如果你向一个函数传入const变量,函数原型中也必须是const的(否则变量需要const_cast类型转换),在调用库函数时这尤其是个麻烦。结论:const变量、数据成员、函数和参数为编译时类型检测增加了一层保障,更好的尽早发现错误。因此,我们强烈建议在任何可以使用的情况下使用const:1) 如果函数不会修改传入的引用或指针类型的参数,这样的参数应该为const;2) 尽可能将函数声明为const,访问函数应该总是const,其他函数如果不会修改任何数据成员也应该是const,不要调用非const函数,不要返回对数据成员的非const指针或引用;3) 如果数据成员在对象构造之后不

30、再改变,可将其定义为const。然而,也不要对const过度使用,像const int * const * const x;就有些过了,即便这样写精确描述了x,其实写成const int* x就可以了。关键字mutable可以使用,但是在多线程中是不安全的,使用时首先要考虑线程安全。const位置:有人喜欢int const *foo形式不喜欢const int* foo,他们认为前者更加一致因此可读性更好:遵循了const总位于其描述的对象(int)之后的原则。但是,一致性原则不适用于此,“不要过度使用”的权威抵消了一致性使用。将const放在前面才更易读,因为在自然语言中形容词(const

31、)是在名词(int)之前的。这是说,我们提倡const在前,并不是要求,但要兼顾代码的一致性!12. 整型(Integer Types)C+内建整型中,唯一用到的是int,如果程序中需要不同大小的变量,可以使用中的精确宽度(precise-width)的整型,如int16_t。定义:C+没有指定整型的大小,通常人们认为short是16位,int是32位,long是32位,long long是64位。优点:保持声明统一。Google C+编程规范26缺点:C+中整型大小因编译器和体系结构的不同而不同。结论:定义了int16_t、uint32_t、int64_t等整型,在需要确定大小的整型时可以使

32、用它们代替short、unsigned long long等,在C整型中,只使用 int。适当情况下,推荐使用标准类型如size_t和ptrdiff_t。最常使用的是,对整数来说,通常不会用到太大,如循环计数等,可以使用普通的int。你可以认为int至少为32位,但不要认为它会多于32位,需要64位整型的话,可以使用int64_t或uint64_t。对于大整数,使用int64_t。不要使用uint32_t等无符号整型,除非你是在表示一个位组(bit pattern)而不是一个数值。即使数值不会为负值也不要使用无符号类型,使用断言(assertion,译者注,这一点很有道理,计算机只会根据变量、

33、返回值等有无符号确定数值正负,仍然无法确定对错)来保护数据。无符号整型:有些人,包括一些教科书作者,推荐使用无符号类型表示非负数,类型表明了数值取值形式。但是,在C语言中,这一优点被由其导致的 bugs所淹没。看看:for (unsigned int i = foo.Length()-1; i = 0; -i) .上述代码永远不会终止!有时gcc会发现该bug并报警,但通常不会。类似的 bug还会出现在比较有符合变量和无符号变量时,主要是C的类型提升机制(type-promotion scheme,C语言中各种内建类型之间的提升转换关系)会致使无符号类型的行为出乎你的意料。因此,使用断言声明变

34、量为非负数,不要使用无符号型。13. 64位下的可移植性(64-bit Portability)代码在64位和32位的系统中,原则上应该都比较友好,尤其对于输出、比较、结构对齐(structure alignment)来说:1) printf()指定的一些类型在32位和64位系统上可移植性不是很好,C99标准定义了一些可移植的格式。不幸的是,MSVC 7.1并非全部支持,而且标准中也有所遗漏。所以有时我们就不得不自己定义丑陋的版本(Google C+编程规范27使用标准风格要包含文件inttypes.h):/ printf macros for size_t, in the style of

35、inttypes.h#ifdef _LP64#define _PRIS_PREFIX “z“#else#define _PRIS_PREFIX#endif/ Use these macros after a % in a printf format string/ to get correct 32/64 bit behavior, like this:/ size_t size = records.size();/ printf(“%“PRIuS“n“, size);#define PRIdS _PRIS_PREFIX “d“#define PRIxS _PRIS_PREFIX “x“#de

36、fine PRIuS _PRIS_PREFIX “u“#define PRIXS _PRIS_PREFIX “X“#define PRIoS _PRIS_PREFIX “o“类型 不要使用 使用 备注void *(或其他指针类型)%lx %p int64_t %qd, %lld %“PRId64“ uint64_t %qu, %llu, %llx %“PRIu64“, %“PRIx64“ size_t %u %“PRIuS“, %“PRIxS“ C99指定%zuptrdiff_t %d %“PRIdS“ C99指定%zd注意宏PRI*会被编译器扩展为独立字符串,因此如果使用非常量的格式化字符串

37、,需要将宏的值而不是宏名插入格式中,在使用宏PRI*时同样可以在%后指定长度等信息。例如,printf(“x = %30“PRIuS“n“, x)在32位Linux上将被扩展为 printf(“x = %30“ “u“ “n“, x),编译器会处理为printf(“x = %30un“, x)。2) 记住sizeof(void *) != sizeof(int) ,如果需要一个指针大小的整数要使用 intptr_t。3) 需要对结构对齐加以留心,尤其是对于存储在磁盘上的结构体。在64位系统中,任何拥有iGoogle C+编程规范28nt64_t/uint64_t成员的类/结构体将默认被处理为8

38、字节对齐。如果 32位和64位代码共用磁盘上的结构体,需要确保两种体系结构下的结构体的对齐一致。大多数编译器提供了调整结构体对齐的方案。gcc中可使用_attribute_(packed) ,MSVC提供了#pragma pack()和_declspec(align()(译者注,解决方案的项目属性里也可以直接设置)。4) 创建64位常量时使用LL或ULL 作为后缀,如:int64_t my_value = 0x123456789LL;uint64_t my_mask = 3ULL 48;5) 如果你确实需要32位和64位系统具有不同代码,可以在代码变量前使用。(尽量不要这么做,使用时尽量使修改

39、局部化)。14. 预处理宏(Preprocessor Macros)使用宏时要谨慎,尽量以内联函数、枚举和常量代替之。宏意味着你和编译器看到的代码是不同的,因此可能导致异常行为,尤其是当宏存在于全局作用域中。值得庆幸的是,C+中,宏不像C中那么必要。宏内联效率关键代码(performance-critical code)可以内联函数替代;宏存储常量可以const变量替代;宏 “缩写”长变量名可以引用替代;使用宏进行条件编译,这个,最好不要这么做,会令测试更加痛苦(#define防止头文件重包含当然是个例外)。宏可以做一些其他技术无法实现的事情,在一些代码库(尤其是底层库中)可以看到宏的某些特性

40、(如字符串化(stringifying,译者注,使用#)、连接(concatenation,译者注,使用#)等等)。但在使用前,仔细考虑一下能不能不使用宏实现同样效果。译者注:关于宏的高级应用,可以参考 C语言宏的高级应用。下面给出的用法模式可以避免一些使用宏的问题,供使用宏时参考:1) 不要在.h文件中定义宏;2) 使用前正确#define,使用后正确 #undef;3) 不要只是对已经存在的宏使用#undef,选择一个不会冲突的名称;4) 不使用会导致不稳定的C+构造( unbalanced C+ Google C+编程规范29constructs,译者注)的宏,至少文档说明其行为。15.

41、 0和NULL(0 and NULL )整数用0,实数用0.0,指针用NULL,字符(串)用0。整数用0,实数用0.0,这一点是毫无争议的。对于指针(地址值),到底是用0还是NULL,Bjarne Stroustrup建议使用最原始的0 ,我们建议使用看上去像是指针的NULL,事实上一些C+ 编译器(如gcc 4.1.0)专门提供了NULL的定义,可以给出有用的警告,尤其是sizeof(NULL)和sizeof(0)不相等的情况。字符(串)用0,不仅类型正确而且可读性好。16. sizeof(sizeof)尽可能用sizeof( varname)代替sizeof( type)。使用sizeof

42、( varname)是因为当变量类型改变时代码自动同步,有些情况下sizeof( type)或许有意义,还是要尽量避免,如果变量类型改变的话不能同步。Struct data;memset(memset(17. Boost库(Boost)只使用Boost中被认可的库。定义:Boost库集是一个非常受欢迎的、同级评议的(peer-reviewed)、免费的、开源的C+库。优点:Boost代码质量普遍较高、可移植性好,填补了C+标准库很多空白,如型别特性(type traits)、更完善的绑定(binders)、更好的智能指针,同时还提供了TR1(标准库的扩展)的实现。缺点:某些Boost库提倡的编

43、程实践可读性差,像元程序( metaprogramming)和其他高级模板技术,以及过度“函数化”(“functional“)的编程风格。结论:为了向阅读和维护代码的人员提供更好的可读性,我们只允许使用Boost特性的一个Google C+编程规范30成熟子集,当前,这些库包括:1) Compressed Pair:boost/compressed_pair.hpp;2) Pointer Container:boost/ptr_container不包括ptr_array.hpp和序列化(serialization)。我们会积极考虑添加可以的Boost特性,所以不必拘泥于该规则。_译者:关于C+

44、特性的注意事项,总结一下:1. 对于智能指针,安全第一、方便第二,尽可能局部化(scoped_ptr);2. 引用形参加上const,否则使用指针形参;3. 函数重载的使用要清晰、易读;4. 鉴于容易误用,禁止使用缺省函数参数(值得商榷);5. 禁止使用变长数组;6. 合理使用友元;7. 为了方便代码管理,禁止使用异常(值得商榷);8. 禁止使用RTTI,否则重新设计代码吧;9. 使用C+风格的类型转换,除单元测试外不要使用dynamic_cast;10. 使用流还printf + read/write,it is a problem;11. 能用前置自增/减不用后置自增 /减;12. const能用则用,提倡const在前;13. 使用确定大小的整型,除位组外不要使用无符号型;14. 格式化输出及结构对齐时,注意32位和64位的系统差异;15. 除字符串化、连接外尽量避免使用宏;16. 整数用0,实数用0.0,指针用NULL,字符(串)用0;17. 用sizeof(varname)代替sizeof(type);

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

当前位置:首页 > 网络科技 > C/C++资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报