您手上这本书,是世界顶级C++大师Scott Meyers成名之作的第二版。其第一版诞生于1991年。在国际上,本书所引起的反响之大,波及整个计算机技术出版领域,余音至今未绝。几乎在所有C++书籍的推荐名单上,本书都会位于前三名。作者高超的技术把握力,独特的视角、诙谐轻松的写作风格、独具匠心的内容组织,都受到极大的推崇和仿效。甚至连本书简洁明快的命名风格,也有着一种特殊的号召力,我可以轻易列举出一大堆类似名字,比如Meyers本人的More Effective C++和Effective STL,Don Box的Effective COM,Stan Lippman主编的Efficient C++系列,Herb Sutter的Exceptional C++等等。要知道,这可不是出版社的有意安排,而且上面这些作者,同样是各自领域里的绝顶大师,决非人云亦云、欺世盗名之辈。这种奇特的现象,只能解释为人们对这本书衷心的赞美和推崇。
\r\n 然而这样一本掷地有声的C++世界名著,不仅迟迟未能出版简体中文版,而且在国内其声誉似乎也并不显赫。可以说在一年之前,甚至很少有C++的学习者听说过这本书,这实在是一种遗憾。今天,在很多人的辛勤努力之下,这本书终于能够展现在我们的面前,对于真正的C++程序员来说,这确实是一件值得弹冠相庆的事。
\r\n 我是一名普通的C++爱好者,因为机缘巧合,有幸参与了这本书的繁简转译工作,这使我能够比较早地看到本书的原版和繁体中文版。在这里我必须表达对本书中文译者、台湾著名技术作家侯捷先生的敬意和感谢,因为在我看来,这本书的中文版在质量上较其英文版兄长分毫不差,任何人都知道,达到这一点是多么的困难。侯先生以其深厚的技术功底、卓越的语言能力和严谨细致的治学态度,为我们跨越了语言隔阂所带来的理解障碍,完整而生动地将原书的内容与精神表达无遗,更令人钦佩的是,中文版的行文风格与原文也达到了高度的统一,可谓神形兼备,实在令人赞叹!因此我非常乐意向大家推荐这本书,相信它会在带给您带给你技术享受的同时,也带给您阅读的享受。
\r\n 曾经在网络讨论组中间看到这样的说法,C++程序员可以分成两类,读过Effective C++的和没读过的。或许有点夸张了,但无论如何,当您拥有这本书之后,就获得了迅速提升自己C++功力的一个契机。这本书不是读完一遍就可以束之高阁的快餐读物,也不是能够立刻解决手边问题的参考手册,而是需要您去反复阅读体会,极力融入自己思想之中,融入自己每一次敲击键盘的动作之中。C++是真正程序员的语言,背后有着精深的思想与无以伦比的表达能力,这使得它具有类似宗教般的魅力。希望这本书能够帮助您跨越C++的重重险阻,领略高处才有的壮美,做一个成功而快乐的C++程序员。
\r\n
\r\n
译序(侯捷) v\r\n目录(Contents) vii\r\n前言(Preface) xiii\r\n致谢(Acknowledgments. 中文版略) xvii\r\n导读(Introduction) 001\r\n改变旧有的C习惯(Shifting from C to C++) 013\r\n条款1:尽量以 const 和 inline 取代 #define 013\r\nPrefer const and inline to #define.\r\n条款2:尽量以 取代 017\r\nPrefer to .\r\n条款3:尽量以 new 和 delete 取代 malloc() 和 free() 019\r\nPrefer new and delete to malloc and free.\r\n条款4:尽量使用 C++ 风格的注释形式 021\r\nPrefer C++-style comments.\r\n\r\n内存管理(Memory Management) 022\r\n条款5:使用相同形式的 new 和 delete 023\r\nUse the same form in corresponding uses of new and delete.\r\n条款6:记得在 destructor 中以delete 对付 pointer members 024\r\nUse delete on pointer members in destructors.\r\n条款7:为内存不足的状况预做准备 025\r\nBe prepared for out-of-memory conditions.\r\n条款8:撰写 operator new 和 operator delete 时应遵行公约 033\r\nAdhere to convention when writing operator new and operator delete.\r\n条款9:避免遮掩了 new 的正规形式 037\r\nAvoid hiding the 'normal' form of new.\r\n条款10:如果你写了一个operator new,\r\n请对应也写一个operator delete 039\r\nWrite operator delete if you write operator new.\r\n\r\n构造函数、析构函数和Assignment 运算符 049\r\nConstructors, Destructors, and Assignment Operators\r\n条款11:如果classes内动态配置有内存,请为此 class 宣告\r\n一个 copy constructor 和一个 assignment 运算符 049\r\nDeclare a copy constructor and an assignment operator\r\nfor classes with dynamically allocated memory.\r\n条款12:在 constructor 中尽量以 initialization 取代 assignment 052\r\nPrefer initialization to assignment in constructors.\r\n条款13:Initialization list 中的 members 初始化排列次序应该 \r\n和其在 class 内的声明次序相同 057\r\nList members in an initialization list in the order\r\nin which they are declared.\r\n条款14:总是让 base class 拥有 virtual destructor 059\r\nMake sure base classes have virtual destructors.\r\n条款15:令 operator= 传回'*this 的 reference' 064\r\nHave operator= return a reference to *this.\r\n条款16:在 operator= 中为所有的 data members 赋予内容 068\r\nAssign to all data members in operator=.\r\n条款17:在 operator= 中检查是否'自己赋值给自己' 071\r\nCheck for assignment to self in operator=.\r\n\r\n类与函数之设计和声明 077\r\nClasses and Functions: Design and Declaration\r\n条款18:努力让接口完满且最小化 079\r\nStrive for class interfaces that are complete and minimal.\r\n条款19:区分member functions, non-member functions和 \r\nfriend functions三者 084\r\nDifferentiate among member functions, non-member functions,\r\nand friend functions.\r\n条款20:避免将 data members 放在公开接口中 089\r\nAvoid data members in the public interface.\r\n条款21:尽可能使用 const 091\r\nUse const whenever possible.\r\n条款22:尽量使用 pass-by-reference(传址), \r\n少用 pass-by-value(传值) 098\r\nPrefer pass-by-reference to pass-by-value.\r\n条款23:当你必须传回object 时,不要尝试传回 reference 101\r\nDon’t try to return a reference when you must return an object.\r\n条款24:在函数重载(function overloading)和\r\n参数缺省化(parameter defaulting)之间,谨慎抉择 106\r\nChoose carefully between function overloading\r\nand parameter defaulting.\r\n条款25:避免对指针型别和数值型别进行重载 109\r\nAvoid overloading on a pointer and a numerical type.\r\n条款26:防卫潜伏的 ambiguity(模棱两可)状态 113\r\nGuard against potential ambiguity.\r\n条款27:如果不想使用编译器暗自产生的 member functions, \r\n就应该明白拒绝它 116\r\nExplicitly disallow use of implicitly generated member functions\r\nyou don’t want.\r\n条款28:尝试切割 global namespace(全域命名空间) 117\r\nPartition the global namespace.\r\n\r\n类与函数之实现 123\r\nClasses and Functions: Implementation\r\n条款29:避免传回内部数据的 handles 123\r\nAvoid returning 'handles' to internal data.\r\n条款30:避免写出member function,传回一个 non-const pointer 或\r\nreference 并以之指向较低存取层级的 members 129\r\nAvoid member functions that return non-const pointers or\r\nreferences to members less accessible than themselves.\r\n条款31:千万不要传回'函数内 local 对象的 reference', \r\n或是'函数内以 new 获得的指针所指的对象' 131\r\nNever return a reference to a local object or to\r\na dereferenced pointer initialized by new within the function.\r\n条款32:尽可能延缓变量定义式的出现 135\r\nPostpone variable definitions as long as possible.\r\n条款33:明智地运用 inlining 137\r\nUse inlining judiciously.\r\n条款34:将文件之间的编译依赖关系(compilation dependencies)\r\n降至最低 143\r\nMinimize compilation dependencies between files.\r\n\r\n继承机制与面向对象设计 153\r\nInheritance and Object-Oriented Design\r\n条款35:确定你的 public inheritance 模塑出 'isa' 的关系 154\r\nMake sure public inheritance models 'isa.'\r\n条款36:区分'接口继承(interface inheritance)'和\r\n'实现继承(implementation inheritance)' 161\r\nDifferentiate between inheritance of interface and\r\ninheritance of implementation.\r\n条款37:绝对不要重新定义一个继承而来的非虚拟函数 169\r\nNever redefine an inherited nonvirtual function.\r\n条款38:绝对不要重新定义一个继承而来的缺省参数值 171\r\nNever redefine an inherited default parameter value.\r\n条款39:避免在继承体系中做 cast down(向下转型)的动作 173\r\nAvoid casts down the inheritance hierarchy.\r\n条款40:通过 layering(分层技术)来模塑 has-a 或\r\nis-implemented-in-terms-of 的关系 182\r\nModel 'has-a' or 'is-implemented-in-terms-of' through layering.\r\n条款41:区分 inheritance 和 templates 185\r\nDifferentiate between inheritance and templates.\r\n条款42:明智地运用 private inheritance(私有继承) 189\r\nUse private inheritance judiciously.\r\n条款43:明智地运用多继承(multiple inheritance,MI) 194\r\nUse multiple inheritance judiciously.\r\n条款44:说出你的意思并了解你所说的每一句话 210\r\nSay what you mean; understand what you’re saying.杂项讨论(Miscellany) 212\r\n条款45:知道 C++(编译器)默默为我们完成和调用哪些函数 212\r\nKnow what functions C++ silently writes and calls.\r\n条款46:宁愿编译和连接时出错,也不要执行时才错 216\r\nPrefer compile-time and link-time errors to runtime errors. 219\r\nEnsure that non-local static objects are initialized\r\nbefore they’re used.\r\n条款48:不要对编译器的警告讯息视而不见 223\r\nPay attention to compiler warnings.\r\n条款49:尽量让自己熟悉 C++ 标准程序库 224\r\nFamiliarize yourself with the standard library.\r\n条款50:加强自己对 C++ 的了解 232\r\nImprove your understanding of C++.\r\n
这本书是多年来我对专业程序员所进行的C++课程教学的一个附产物。我发现,大部分学生在一个星期的密集训练之后,即可适应这个语言的基本架构,但要他们“将这些基础架构以有效的方式组合运用”,我实在不感乐观。于是我开始尝试组织出一些简短、明确、容易记忆的准则,作为c++高效性程序开发过程之用。那都是经验丰富的C++程序员几乎总是会奉行或几乎肯定要避免的一些事情。
我最初的兴趣在于整理出一些可被某种“lint-like程序”施行的规则,最后我甚至领导一个计划,研究某种可将C++原始代码中违反用户指定条件之处检验出来的工具’。不幸的是在我尚未完成其完整原型之前,这个研究计划便结束了。幸运的是,目前市面上已有这类C++检验工具(商品),而且不只一个。
虽然我最初的兴趣是在研究可被(某种工具)自动实施的程序设计准则,但我很快了解到那个研究方向的局限性。优秀的C++程序员所奉行的准则,多数都难以“公式化”;要不就是虽然它们有许多重要的例外情况,却被程序员盲目地奉行不渝。这使我念头一转:某些东西虽然不比计算机程序精准,但仍能比一本泛泛的C++教科书更集中火力,更能抓住重点。这个念头的结果就是你手上的这本书:一本内含50个有效建议(如何改善你的C++程序技术和你的设计思维)的书。
在这本书中,你会发现一些忠告,告诉你应该做些什么,为什么如此;告诉你不应该做些什么,又为什么如此。基本而言,当然whys比whats更重要,但检阅一个个准则,也确实比强记一本或两本教科书更轻松、更方便得多。
和大部分的c++书籍不同,我的组织方式并非以语言特性作为依据。也就是说,我并不在某处集中讨论constructors(构造函数),在另一处集中讨论virtualfunctions(虚拟函数),又在第三个地方集中讨论inheritance(继承关系)。不,不是这样,本书的每一个讨论主题都剪裁合度地以一个个准则陈列出来。至于我对某个特定语言性质的探讨,散布面积可能涵盖整本书。
这种做法的优点就是比较容易反映出“特意挑选C++作为开发工具”的那些软件系统的复杂度。在那些系统之中,仅只了解个别语言特性是不够的。例如,有经验的C++程序员知道,了解inline函数和了解virtual destructors,并不一定表示你了解inline virtual destructors。身经百战的开发人员都认识到,理解C++各个特性之间的互动关系,才是有效使用这个语言最重要的关键之处。本书的组织结构反映出了这一基本事实。
这种做法的缺点是,你恐怕必须前后交叉参考而非只看一个地方,才能发现我所说的某个c++架构的全貌。为了将不方便性降至最低,我在书中各处放了许多交叉索引,书后并有一份涵盖全部范围的索引。 (译注:为了协助读者更容易掌握Effective C++和More Effective C++二书, 我以Effective C++CD为范本,为两书的中文版额外加上两书之间的交叉索引。此乃原书所无。如果文中出现像条款M5这样的参考指示,M便是代表More Effective C++)
筹划第二版期间,我改写此书的雄心一再被恐惧所取代。成千上万的程序员热情拥抱Effective C++第一版,我不希望破坏吸引他们的任何东西。但是自从我写了第一版之后,六年过去了,C++有了变化,C++程序库有了变化(见条款49),我对C++的了解也有了变化,乃至于C++的用途也有了变化。许许多多的变化。对我而言,重要的是我必须修订EffectiveC++以反映那些变化。我尝试一页——页地修改内容,但是书籍和软件十分类似,局部加强是不够的,唯一的机会就是系统化地重写。本书就是重写后的结果:Effective C++2.0版。
熟悉第一版的读者可能有兴趣知道,书中的每一项条款都经过重新检验。然而我相信第一版的结构至今仍是流畅的,所以整本书的结构开没有改变。50项条款中,我保留了48项,其中某些标题稍有变化(附随的讨论内容亦复如此)。被取代的两个条款是32和49,不过原条款32的许多信息被我移到如今焕然一新的条款1中。我将条款41和42的次序做了对调,因为这样更能够适当地呈现它们修订后的内容。最后,我把上一版继承体系图所采用的箭头方向颠倒过来,以符合目前几乎已经一致的习惯:从derived classes指往base classes。我的More Effective C++一书也采用相同习惯(本书最后列有该书摘要)。
本书提供的准则,离巨细靡遗的程度还很远,但是完成一个好的准则——一个几乎可于任何时间应用于任何程序的准则,动手远比动嘴困难得多。如果你知道其它准则,可以协助撰写有效的C++程序,我非常乐意听到你告诉我它们的故事。
此外,说不定你会觉得本书的某些条款不适合成为一般性忠告;或许你认为另有比较好的方法来完成书中所说的任务;或许你认为某些条款在技术讨论方面不够清楚,不够完全,抑或有误导之嫌。我衷心盼望你也能够让我知道你的这些想法。
Donald Knuth(译注:经典书籍The Art of Computer Programming,Volume I,II,III的作者)长久以来为挑出其书错误的热心读者准备有一份小小的报酬。这个故事传为美谈。追求完美的精神令人佩服。看过那么多仓促上市错误累累的c++书籍后,我更是特别强烈地希望追随Knuth的风范。因此,如果有人挑出本书的任何错误并告诉我——不论是技术、文法、错别字或任何其它东西——我将在本书重新印刷的时候,把第一位挑出错误的读者大名加到致谢名单中。
请将你的建议、你的见解、你的批评,以及(喔,真糟……)你的臭虫报告,寄至:
Scott Meyers
c/o Publisher, Corporate and Professional Publishing
Addison Wesley Longman, Inc.
1 Jacob Way
Reading, MA 01867
U.S.A
或者传送电子邮件到ec++@awl.com。
我维护有本书第一次印刷以来的修订记录,其中包括错误更正、文字修润,以及技术更新。你可以从Effective C++网站取得这份记录。如果你希望拥有这份资料,但无法上网,请寄申请函到上术地址,我会邮寄一份给你。