logo资料库

More Effective C++.pdf

第1页 / 共264页
第2页 / 共264页
第3页 / 共264页
第4页 / 共264页
第5页 / 共264页
第6页 / 共264页
第7页 / 共264页
第8页 / 共264页
资料共264页,剩余部分请下载后查看
More Effecitve C++ : Scott Meyers 作者 译序、导读 : 侯捷 Item 1~28 : ZHC Item 29~35 : WQ 附 1 附 2 附 3、附 4 : 侯捷 : WQ : 陈崴 1. 译序(侯捷) ................................................................................................................................3 2. 导读 ................................................................................................................................................4 2.1 本书所谈的 C++ .................................................................................................................4 2.2 惯例与术语..........................................................................................................................6 2.3 臭虫报告,意见提供,内容更新 ....................................................................................7 3. 基础议题 ........................................................................................................................................8 ITEM M1:指针与引用的区别...............................................................................................8 ITEM M2:尽量使用 C++风格的类型转换........................................................................10 ITEM M3:不要对数组使用多态.........................................................................................14 ITEM M4:避免无用的缺省构造函数 ................................................................................16 4. 运算符 ..........................................................................................................................................20 3.1 3.2 3.3 3.4
4.1 4.2 4.3 4.4 ITEM M5:谨慎定义类型转换函数.....................................................................................21 ITEM M6:自增(INCREMENT)、自减(DECREMENT)操作符前缀形式与后缀形式的区别 27 ITEM M7:不要重载“&&”,“||”, 或“,”.............................................................................29 ITEM M8:理解各种不同含义的 NEW 和 DELETE .............................................................32 5. 异常 ..............................................................................................................................................37 ITEM M9:使用析构函数防止资源泄漏 ............................................................................38 ITEM M10:在构造函数中防止资源泄漏 ..........................................................................42 ITEM M11:禁止异常信息(EXCEPTIONS)传递到析构函数外 .....................................51 ITEM M12:理解“抛出一个异常”与“传递一个参数”或“调用一个虚函数”间的 6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8 6.9 ITEM M13:通过引用(REFERENCE)捕获异常 ...............................................................60 ITEM M14:审慎使用异常规格(EXCEPTION SPECIFICATIONS) ..........................................64 ITEM M15:了解异常处理的系统开销 ..............................................................................69 6. 效率 ..............................................................................................................................................71 ITEM M16:牢记 80-20 准则(80-20 RULE) ..............................................................72 ITEM M17:考虑使用 LAZY EVALUATION(懒惰计算法) ..............................................74 ITEM M18:分期摊还期望的计算.......................................................................................81 ITEM M19:理解临时对象的来源.......................................................................................85 ITEM M20:协助完成返回值优化.......................................................................................87 ITEM M21:通过重载避免隐式类型转换 ..........................................................................91 ITEM M22:考虑用运算符的赋值形式(OP=)取代其单独形式(OP)......................93 ITEM M23:考虑变更程序库...............................................................................................96 ITEM M24:理解虚拟函数、多继承、虚基类和 RTTI 所需的代价..............................98 7. 技巧(TECHNIQUES,又称 IDIOMS 或 PATTERN).....................................................106 ITEM M25:将构造函数和非成员函数虚拟化................................................................107 ITEM M26:限制某个类所能产生的对象数量................................................................ 111 ITEM M27:要求或禁止在堆中产生对象 ........................................................................125 ITEM M28:灵巧(SMART)指针......................................................................................134 ITEM M29:引用计数..........................................................................................................149 ITEM M30:代理类..............................................................................................................177 ITEM M31:让函数根据一个以上的对象来决定怎么虚拟...........................................190 8. 杂项 ............................................................................................................................................212 ITEM M32:在未来时态下开发程序.................................................................................212 ITEM M33:将非尾端类设计为抽象类 ............................................................................216 ITEM M34:如何在同一程序中混合使用 C++和 C........................................................226 ITEM M35:让自己习惯使用标准 C++语言....................................................................231 9. 附录 ............................................................................................................................................237 9.1 推荐读物 ..........................................................................................................................237 9.2 一个 AUTO_PTR 的实现实例 ..........................................................................................241 9.3 在 C++ 中计算物件个数(OBJECTS COUNTING IN C++)译者:陈崴...................244 9.4 为智能指标实作 OPERATOR->*(IMPLEMENTING OPERATOR->* FOR SMART POINTERS) 译者:陈崴 ....................................................................................................................................254 7.1 7.2 7.3 7.4 7.5 7.6 7.7 5.1 5.2 5.3 5.4 差异 54 5.5 5.6 5.7 8.1 8.2 8.3 8.4
1. 译序(侯捷) C++ 是一个难学易用的语言! C++ 的难学,不仅在其广博的语法,以及语法背後的语意,以及语意背後的深层思维, 以及深层思维背後的物件模型;C++ 的难学,还在於它提供了四种不同(但相辅相成)的程 式设计思维模式:procedural-based,object-based,object-oriented,generic paradigm。 世上没有白吃的午餐。又要有效率,又要有弹性,又要前瞻望远,又要回溯相容,又要 能治大国,又要能烹小鲜,学习起来当然就不可能太简单。 在如此庞大复杂的机制下,万千使用者前仆後续的动力是:一旦学成,妙用无穷。C++ 相 关书籍之多,车载斗量;如天上繁星,如过江之鲫。广博如四库全书者有之(The C++ Programming Language、C++ Primer),深奥如重山复水者有之(The Annotated C++ Reference Manual, Inside the C++ Object Model),细说历史者有之(The Design and Evolution of C++, Ruminations on C++),独沽一味者有之(Polymorphism in C++, Genericity in C++), 独树一帜者有之(Design Patterns,Large Scale C++ Software Design, C++ FAQs),程式 库大全有之(The C++ Standard Library),另辟蹊径者有之(Generic Programming and the STL),工程经验之累积亦有之(Effective C++, More Effective C++, Exceptional C++)。 这其中,「工程经验之累积」对已具 C++ 相当基础的程式员而言,有著致命的吸引力与 立竿见影的帮助。Scott Meyers 的 Effective C++ 和 More Effective C++ 是此类佼佼, Herb Sutter 的 Exceptional C++ 则是後起之秀。 这类书籍的一个共通特色是轻薄短小,并且高密度地纳入作者浸淫於 C++/OOP 领域多 年而广泛的经验。它们不但开展读者的视野,也为读者提供各种 C++/OOP 常见问题或易犯 错误的解决模型。某些小范围主题诸如「在 base classes 中使用 virtual destructor」、 「令 operator= 传回*this 的 reference」,可能在百科型 C++ 语言书籍中亦曾概略提过, 但此类书籍以深度探索的方式,让我们了解问题背後的成因、最佳的解法、以及其他可能的 牵扯。至於大范围主题,例如 smart pointers, reference counting, proxy classes,double dispatching, 基本上已属 design patterns 的层级! 这些都是经验的累积和心血的结晶。 我很高兴将以下三本极佳书籍,规划为一个系列,以精装的形式呈现给您: 1. Effective C++ 2/e, by Scott Meyers, AW 1998 2. More Effective C++, by Scott Meyers, AW 1996 3. Exceptional C++, by Herb Sutter, AW 1999 不论外装或内容,中文版比其英文版兄弟毫不逊色。本书不但与原文本页页对译, 保留索引,并加上精装、书签条、译注、书籍交叉参考 1、完整范例码 2、读者服务 3。 这套书对於您的程式设计生涯,可带来重大帮助。制作这套书籍使我感觉非常快 乐。我祈盼(并相信)您在阅读此书时拥有同样的心情。 侯捷 2000/05/15 于新竹.台湾 jjhou@ccca.nctu.edu.tw http://www.jjhou.com 1 Effective C++ 2/e 和 More Effective C++ 之中译,事实上是以 Scott Meyers 的另一 个产品 Effective C++ CD 为本,不仅资料更新,同时亦将 CD 版中两书之交叉参考保留下 来。这可为读者带来旁徵博引时的莫大帮助。 2 书中程式多为片段。我将陆续完成完整的范例程式,并在 Visual C++,C++Builder,GNU C++ 上测试。请至侯捷网站(http://www.jjhou.com)下载。 3 欢迎读者对本书范围所及的主题提出讨论,并感谢读者对本书的任何误失提出指正。 来信请寄侯捷电子信箱(jjhou@ccca.nctu.edu.tw)。
2. 导读 对 C++ 程式员而言,日子似乎有点过於急促。虽然只商业化不到 10 年,C++ 却俨然成 为几乎所有主要电算环境的系统程式语言霸主。面临程式设计方面极具挑战性问题的公司和 个人,不断投入 C++ 的怀抱。而那些尚未使用 C++ 的人,最常被询问的一个问题则是:你 打算什么时候开始用 C++。C++ 标准化已经完成,其所附带之标准程式库幅员广大,不仅涵 盖 C 函式库,也使之相形见绌。这么一个大型程式库使我们有可能在不必牺牲移植性的情 况下,或是在不必从头撰写常用演算法和资料结构的情况下,完成琳琅满目的各种复杂程式。 C++ 编译器的数量不断增加,它们所供应的语言性质不断扩充,它们所产生的码品质也不断 改善。C++ 开发工具和开发环境愈来愈丰富,威力愈来愈强大,稳健强固(robust)的程度 愈来愈高。商业化程式库几乎能够满足各个应用领域中的写码需求。 一旦语言进入成熟期,而我们对它的使用经验也愈来愈多,我们所需要的资讯也就随之 改变。1990 年人们想知道 C++ 是什么东西。到了 1992 年,他们想知道如何运用它。如今 C++ 程式员问的问题更高级:我如何能够设计出适应未来需求的软体?我如何能够改善程式 码的效率而不折损正确性和易用性?我如何能够实作出语言未能直接支援的精巧机能? 这本书中我要回答这些问题,以及其他许多类似问题。 本书告诉你如何更具实效地设计并实作 C++ 软体:让它行为更正确;面对异常情况时 更稳健强固;更有效率;更具移植性;将语言特性发挥得更好;更优雅地调整适应;在「混 合语言」开发环境中运作更好;更容易被正确运用;更不容易被误用。简单地说就是如何让 软体更好。 本书内容分为 35 个条款。每个条款都在特定主题上精简摘要出 C++ 程式设计社群所累 积的智慧。大部份条款以准则的型式呈现,附随的说明则阐述这条准则为什么存在,如果不 遵循会发生什么後果,以及什么情况下可以合理违反该准则。所有条款被我分为数大类。某 些条款关心特定的语言性质,特别是你可能罕有使用经验的一些新性质。例如条款 9~15 专 注於 exceptions(就像 Tom Cargill, Jack Reeves, Herb Sutter 所发表的那些杂志文章 一样)。其他条款解释如何结合语言的不同特性以达成更高阶目标。例如条款 25~31 描述如 何限制物件的个数或诞生地点,如何根据一个以上的物件型别产生出类似虚拟函式的东西, 如何产生 smart pointers 等等。其他条款解决更广泛的题目。条款 16~24 专注於效率上的 议题。不论哪一条款,提供的都是与其主题相关且意义重大的作法。在 More Effective C++ 一书中你将学习到如何更实效更精锐地使用 C++。大部份 C++ 教科书中对语言性质的大量描 述,只能算是本书的一个背景资讯而已。 这 种 处 理 方 式 意 味 , 你 应 该 在 阅 读 本 书 之 前 便 熟 悉 C++ 。 我 假 设 你 已 了 解 类 别 (classes)、保护层级(protection levels)、虚拟函式、非虚拟函式,我也假设你已通晓 templates 和 exceptions 背後的概念。我并不期望你是一位语言专家,所以涉及较罕见的 C++ 特性时,我会进一步做解释。 2.1 本书所谈的 C++ 我在本书所谈、所用的 C++,是 ISO/ANSI 标准委员会於 1997 年 11 月完成的 C++国际 标准最後草案(Final Draft International Standard)。这暗示了我所使用的某些语言特 性可能并不在你的编译器(s) 支援能力之列。别担心,我认为对你而言唯一所谓「新」特性, 应该只有 templates,而 templates 如今几乎已是各家编译器的必备机能。我也运用 exceptions,并大量集中於条款 9~15。如果你的编译器(s) 未能支援 exceptions,没什么 大 不 了 , 这 并 不 影 响 本 书 其 他 部 份带 给 你 的 好处 。 但 是 ,听 我 说 , 纵使 你 不 需 用 到 exceptions,亦应阅读条款 9~15,因为那些条款(及其相关篇幅)检验了某些不论什么场 合下你都应该了解的主题。 我承认,就算标准委员会授意某一语言特性或是赞同某一实务作法,并非就保证该语言 特性已出现在目前的编译器上,或该实务作法已可应用於既有的开发环境上。一旦面对「标 准委员会所议之理论」和「真正能够有效运作之实务」间的矛盾,我便两者都加以讨论,虽 然我其实比较更重视实务。由於两者我都讨论,所以当你的编译器(s) 和 C++ 标准不一致 时,本书可以协助你,告诉你如何使用目前既有的架构来模拟编译器(s) 尚未支援的语言特
性。而当你决定将一些原本绕道而行的解决办法以新支援的语言特性取代时,本书亦可引导 你。 注意当我说到编译器(s) 时,我使用复数。不同的编译器对 C++ 标准的满足程度各不 相同,所以我鼓励你在至少两种编译器(s) 平台上发展程式码。这么做可以帮助你避免不经 意地依赖某个编译器专属的语言延伸性质,或是误用某个编译器对标准规格的错误阐示。这 也可以帮助你避免使用过度先进的编译器技术,例如独家厂商才做得出的某种语言新特性。 如此特性往往实作不够精良(臭虫多,要不就是表现迟缓,或是两者兼具),而且 C++ 社群 往往对这些特性缺乏使用经验,无法给你应用上的忠告。雷霆万钧之势固然令人兴奋,但当 你的目标是要产出可靠的码,恐怕还是步步为营(并且能够与人合作)得好。 本书用了两个你可能不甚熟悉的 C++ 性质,它们都是晚近才加入 C++ 标准之中。某些 编译器支援它们,但如果你的编译器不支援,你可轻易以你所熟悉的其他性质来模拟它们。 第一个性质是型别 bool,其值必为关键字 true 或 false。如果你的编译器尚未支援 bool,有两个方法可以模拟它。第一个方法是使用一个 global enum:enum bool { false, true }; 这允许你将参数为 bool 或 int 的不同函式加以多载化(overloading)。缺点是,内建的「比 较运算子(comparison operators)」如==, <, >=, 等等仍旧传回 ints。 所以以下程式码的行为不如我们所预期: void f(int); void f(bool); int x, y; ... f( x < y ); // 呼叫 f(int),但其实它应该呼叫 f(bool) 一旦你改用真正支援 bool 的编译器,这种 enum 近似法可能会造成程式行为的 改变。 另一种作法是利用 typedef 来定义 bool,并以常数物件做为 true 和 false: typedef int bool; const bool false = 0; const bool true = 1; 这种手法相容於传统的 C/C++ 语意。使用这种模拟法的程式,在移植到一个支援有 bool 型别的编译器平台之後,行为并不会改变。缺点则是无法在函式多载化(overloading)时 区分 bool 和 int。以上两种近似法都有道理,请选择最适合你的一种。 第二个新性质,其实是四个转型运算子:static_cast, const_cast, dynamic_cast, 和 reinterpret_cast。如果你不熟悉这些转型运算子,请翻到条款 2 仔细阅读其中内容。 它们不只比它们所取代的 C 旧式转型做得更多,也更好。书中任何时候当我需要执行转型 动作,我都使用新式的转型运算子。 C++ 拥有比语言本身更丰富的东西。是的,C++ 还有一个伟大的标准程式库(见条款 E49)。我尽可能使用标准程式库所提供的 string 型别来取代 char* 指标,而且我也鼓励你 这么做。string objects 并不比 char*-based 字串难操作,它们的好处是可以免除你大部 份的记忆体管理工作。而且如果发生 exception 的话(见条款 9 和 10),string objects 比 较没有 memory leaks(记忆体遗失)的问题。 实作良好的 string 型别甚至可和对应的 char* 比赛效率,而且可能会赢(条款 29 会 告诉你个中故事)。如果你不打算使用标准的 string 型别,你当然会使用类似 string 的其 他 classes,是吧?是的,用它,因为任何东西都比直接使用 char* 来得好。 我将尽可能使用标准程式库提供的资料结构。这些资料结构来自 Standard Template Library("STL" — 见条款 35)。STL 包含 bitsets, vectors, lists, queues,stacks, maps, sets, 以及更多东西,你应该尽量使用这些标准化的资料结构,不要情不自禁地想写一个自 己的版本。你的编译器或许没有附 STL 给你,但不要因为这样就不使用它。感谢 Silicon Graphics 公司的热心,你可以从 SGI STL 网站下载一份免费产品,它可以和多种编译器搭 配。 如果你目前正在使用一个内含各种演算法和资料结构的程式库,而且用得相当愉快,那 么就没有必要只为了「标准」两个字而改用 STL。然而如果你在「使用 STL」和「自行撰写
同等功能的码」之间可以选择,你应该让自己倾向使用 STL。记得程式码的重用性吗?STL (以及标准程式库的其他组件)之中有许多码是十分值得重复运用的。 2.2 惯例与术语 任何时候如果我谈到 inheritance(继承),我的意思是 public inheritance(见条款 E35)。如果我不是指 public inheritance,我会明白地指明。绘制继承体系图时,我对 base-derived 关系的描述方式,是从 derived classes 往 base classes 画箭头。 例如,下面是条款 31 的一张继承体系图: GameObject SpaceShip Asteroid SpaceStation 这样的表现方式和我在 Effective C++ 第一版(注意,不是第二版)所采用的习惯不 同。现在我决定使用这种最广被接受的继承箭头画法:从 derived classes 画往 base classes,而且我很高兴事情终能归於一统。此类示意图中,抽象类别(abstract classes, 例如上图的 GameObject)被我加上阴影而具象类别(co SpaceShip)未加阴影。 ncrete classes,例如上图的 Inheritance(继承机制)会引发「pointers(或 references)拥有两个不同型别」 的 type)。Pointer 或 由它们实际所指的物件来 type)和动态型别(dynamic 议题,两个型别分别是静态型别(static reference 的「静态型别」是指其宣告时的型别,「动态型别」则 决定。下面是根据上图所写的一个例子: GameObject *pgo = // pgo 的静态型别是 GameObject*, new SpaceShip; // 动态型别是 Asteroid *pa = new Asteroid; // pa 的静态型别是 Asteroid*, // 动态型别也是 Asteroid*。 pgo = pa; // pgo 的静态型别仍然(永远)是 GameObject*, // 至於其动态型别如今是 Asteroid*。 GameObject& rgo = *pa; // rgo 的静态型别是 GameObject, // 动态型别是 Asteroid。 SpaceShip* 这些例子也示范了我喜欢的一种命名方式。p go 是一个 pointer-to-GameObject;pa 式来 是一个 pointer-to-Asteroid;rgo 是一个 reference-to-GameObject。我常常以此方 为 pointer 和 reference 命名。 我很喜欢两个参数名称:lhs 的缩写。为了了解这些名称背後的基本原理, 的 cla class Rational { . 和"right-hand side" 请考虑一个用来表示分数(rational numbers) 和 rhs,它们分别是"left-hand side" .. }; ss: 如果我想要一个用以比较两个 Rational objects 的函式,我 可能会这样宣告: bool operator==(const Rational& lhs, const Rational& rhs); 这使我得以写出这样的码: Rational r1, r2; ... if (r1 == r2) ... 在呼叫 operator== 的过程中,r1 位於"==" 左侧,被系结於 lhs,r2 位於"=="右侧, 被系结於 rhs。 我使用的其他缩写名称还包括:ctor 代表"constructor",dtor 代表"destructor", 是 RTTI 代表 C++ 对 runtime type identification 的支援(在此性质中,dynamic_cast 最常被使用的一个零组件)。
当你配置记忆体而没有释放它,你就有了 memory leak(记忆体遗失)问题。Memory leaks 和 C++ 中都有,但是在 C++ 中,memory leaks 所遗失的还不只是记忆体,因为 在 C C++ 会 在 物 件 被 产 生 时 , 自 动 呼 叫 constructors 而 constructors 本 身 可 能 亦 配 有 资 源 (resources)。举个例子,考虑以下程式码: class Widget { ... }; // 某个 class — 它是什么并不重要。 Widget *pw = new Widget; // 动态配置一个 Wid ... // 假设 pw 一直未被删除(deleted)。 这段码会遗失记忆体,因为 pw 所指的 Widget 物件从未被删除。如果 Widget get 物件。 , constructor 配 置 了 其 他 资 源 ( 例 如 file descriptors, semaphores, window handles,database locks),这些资源原本应该在 Widget 物件被摧毁时释放,现在也像记 中 忆体一样都遗失掉了。为了强调在 C++ 中 memory leaks 往往也会遗失其他资源,我在书 常以 resource leaks 一词取代 memory leaks。 你不会在本书中看到许多 inline 函式。并不是我不喜欢 inlining,事实上我相信 inline 是个坏主意,而只是说,它「是否为 inline」与当时讨论的主题无关。 inline 函式是 C++ 的一项重要性质。然而决定一个函式是否应被 inlined,条件十分复杂、 敏感、而且与平台有关(见条款 E33)。所以我尽量避免 inlining,除非其中有个关键点非 使用 inlining 不可 。当你在本书之中看到一个 non-inline 函式,并不意味我认为把它宣 告为 有一些传统的 C++ 性质已明白地被标准委员会排除。这样的性质被明列於语言的最後撤除 名单,因为新性质已经加入,取代那些传统性质的原本工作,而且做得更好。这本书中我会 检视被撤除的性质,并说明其取代者。你应该避免使用被撤除的性质 ,但是过度在意倒亦不 必, 因为编译器厂商为了挽留其客户,会尽力保存回溯相容性,所以那些被撤除的性质大约 还会存活好多年。 所谓 client,是指你所写的程式码的客户。或许是某些人(程式员),或许是某些物 (classes 或 functions)。举个例子,如果你写了一个 Date class(用来表现生日、最後 等),任何使用了这个 class 的人,便是你的 client。任何一段使 期限、耶稣再次降临日等 用了 Date class 的码,也是你的 clients。Clients 是重要的。 事实上 clients 是游戏的主角。如果没有人使用你写的软体,你又何必写它呢?你会 发现我很在意如何让 clients 更轻松,通常这会导至你的行事更困难,因为好的软体「以 客为尊」。如果你讥笑我太过滥情,不妨反躬自省一下。你曾经使用过自己写的 classes 或 functions 吗?如果是,你就是你自己的 client,所以让 clients 更轻松,其实就是让自 己更轻松,利人利己。 当我讨论 class template 或 function templates 以及由它 们所产生出来的 classes 或 functions 时,请容我保留偷懒的权利,不一一写出 templates 和其 instantiations(具 rray 是个 class template,有个型别参数 T,我可 现体)之间的差异。举个例子,如果 A 能会以 Array 代表此 template 的某个特定具现体(instantiation),虽然其实 Array 才 是正 式的 class 名称。同样道理,如果 swap 是个 function template,有个型别参数 T, 我可 能会以 swap 而非 swap 表示其具现体。如果这样的简短表示法在当时情况下不够清 楚,我便会在表示 template 具现体时加上 template 参数。 2.3 臭虫报告,意见提供,内容更新 我尽力让这 如果你发现任何错误— 技术性的、语言上的、错别字、或任何其 本书技术精准、可读性高,而且有用,但是我知道一定仍有改善空间。 他东西— 请告诉我。 第一位告诉我的人,我会很高兴将你的大名登录到 我会试著在本书新刷中修正之。如果你是 本书致谢文(a cknowledgments)内。如果你有改善建议,我也非常欢迎。 我将继续收集 C+ + 程式设计的实效准则。如果你有任何这方面的想法并愿意与我分享, 兴。请将你的建议、你的见解、你的批评、以及你的臭虫报告,寄至: t Meyers Editor-in-Chief, Corporate and Professional Publishing 我会十分高 Scot c/o Addison-Wesley Publishing Company 1 Jacob Way
Reading, MA 01867 U. S . A. 或者你也可以送电子邮件到 mec++@awl.com。 我维护有一 份本书第一刷以来的修订记录,其中包括错误修正、文字修润、以及技术更 新。你可以从本书网站取得这份记录,以及其他与本书相关的资讯。你也可以透过 anonymous FTP, 从 ftp.awl.com 的 cp/mec++ 目录中取得它。如果你希望拥有这份资料,但无法上网, 请寄申请函到上述地址,我会邮寄一份给你。 这篇序文有够长的,让我们开始正题吧。 3. 基础议题 基础议题。是的,pointers(指针)、references(引用)、casts(类型转换)、arrays (数组)、constructors(构造)- 再沒有比这些更基础的议题了。几乎最简单的 C++ 程序 也会用到其中大部份特性,而许多程序会用到上述所有特性。 尽管你可能已经十分熟悉语言的这一部份,有时候它们还是会令你吃惊。特别是对那些 从 C 转到 C++ 的程序员,因为 references, dynamic casts, default constructors 及其 它 non-C 性质背后的观念,往往带有一股幽暗阴 郁的色彩。 這一章描述 pointers 和 references 的差异,並告诉你它们的适当使用时机。本章介 解释为什么新式类型转换法比旧式的 C 类型转换法 绍新的 C++ 类型转换(casts)语法,並 优越。本章也检验 C 的数组概念以及 C++ 的多态(polymorphism)概念,並说明为什么將 default constructors(默认构造函数)的 这两者混合运用是不智之举。最后,本章讨论 正方和反方意见,並提出一些建议作法,让你回避语言的束缚(因为在你不需 default cons tructors 的情況下,C++ 也会给你一个。 只要留心下面各条款的各项忠告,你将向著一個很好的目标迈进:你所生产的软件可以 清楚而正确地表現出你的设计意图。 3.1 Item M1:指针与引用的区别 指针与引用看上去完全不同(指针用操作符“*”和“->”,引用使用操作符“. ”),但 是它们似乎有相同的功能。指针与引用都是让你间接引用其他对象。你如何决定在什么时候 使用指针,在什么时候使用引用呢? 首先,要认识到在任何情况下都不能使用指向空值的引用。一个引用必须总是 指向某些 对象 。因此如果你使用一个变量并让它指向一个对象, 但是该变量在某些时候也可能不指向 任何 对象,这时你应该把变量声明为指针,因为这样 你可以赋空值给该变量。相反,如果变 量肯 定指向一个对象,例如你的设计不允许变量为空,这时你就可以把变量声明为引用。 “但是,请等一下”,你怀疑地问,“这样的代码会产生什么样的后果?” char *pc = 0; // 设置指针为空值 char& rc = *pc; // 让引用指向空值 这是非常有害的,毫无疑问。结果将是不确定的(编译器能产生一 些输出,导致任何事 情都 有可能发生)。应该躲开写出这样代码的人,除非他们同意 改正错误。如果你担心这样 的代码会出现在你的软件里,那么你最好完全避免使用引用,要不然就去让更优秀的程序员 去做。我们以后将忽略一个引用指向空值的可能性。 因为引用肯定会指向一个 对象,在 C++里,引用应被初始化。
分享到:
收藏