前言
或许我不像喜欢我的孩子们那样喜爱C++(www.cppentry.com),甚至或许我对C++(www.cppentry.com)的喜爱都比不上我对骑自行车在坡度为32°、光滑度为10%的柏油路上爬坡的热衷, 尽管有时这些喜爱之情的确十分接近。我庆幸我有这样的人生,让我得以将生命中的部分时间用来实践或阐释Frederick P.Brooks的名言:"尽量发挥想象力进行创造"。我更要感激的是我能够跟这门如此强大、危险却又诱人的语言相伴。
这些话听起来似乎蛮华丽动听,但你可能是因为看到本书的书名才买下它的,以为本书是一本抨击C++(www.cppentry.com)的图书。你可能是Java或C或其他主流语言的热衷者,因而买这本书可能是想从中找到支持你远离C++(www.cppentry.com)的理由。倘若果真如此,你会失望的,因为本书并不是举办一切C++(www.cppentry.com)批判大会。不过先别急着离开,因为你或许能够从中找到令你开始接触C++(www.cppentry.com)的理由。
你将从中学到什么
我写这本书的目的在于给予开发同行们一些能量。它以虽批评但具建设性的眼光来看待C++(www.cppentry.com)及其不完美之处,并给出实际的措施以避免或改善这种不完美。我希望你在读完本书后能够对下面这些问题有一个更好的把握:
如何克服C++(www.cppentry.com)类型系统中的某些不足。
模板编程(www.cppentry.com)在提高代码灵活性和健壮性上的强大能力。
如何在(当前标准尚不予置理的)未定义行为的险恶丛林中生存下来。后者包括动态库、静态对象以及线程等。
隐式转换的代价、它所带来的问题,及其替代方案,即基于显式转换的、高效且易于控制的泛用性编程(www.cppentry.com)。
如何编写能够与(或更容易令它们与)其他编译器、库、线程模型等兼容的软件。
编译器"在幕后"都做了些什么?你对编译器可以施加什么样的影响。
数组和指针之间微妙而棘手的互操作性,以及用于防止它们的行为变得彼此相似的技术。
C++(www.cppentry.com)对RAII(Resource Acquisition Is Initialization,资源获取即初始化)机制的支持,以及该机制可以被运用到的各种问题领域。
如何通过最大限度地榨取编译器的侦错能力来节省你自己所需花费的工夫。
有了上面这些"装备",你编写的代码将更有效率、更具可维护性、更健壮,也更具灵活性。
我的意图是,即便是经验丰富的C++(www.cppentry.com)实践者也能从本书中发现新思想和新技术,从而引发他们的灵感并提高其现有的实战能力。经验较少的程序员则能领会其中包含的有关原则并将相关技术应用到自己的工作中,在增长见识的同时弥补自己对一些技术细节理解上的疏漏。
我并不指望你们所有人都完全同意我所说的一切,但我期望哪怕是最有争议的内容也能激发你去真正弄明白自己对这门强大语言的使用问题。
我假设你们已经知道
除非有人想写一本很厚的书,否则他必须假设许多知识是读者已经知道的。当然,规定大家必须首先读完某些书籍的做法过于粗鲁,但我假设你们的知识和经验能使你们轻松地理解Scott Meyer的Effective C++(www.cppentry.com)系列和Herb Sutter的Exceptional C++(www.cppentry.com)系列中讲述的绝大部分概念。我还假设你们拥有一本C++(www.cppentry.com)"圣经",即Bjarne Stroustrup的The C++(www.cppentry.com) Programming Language。我并不指望你们仔细阅读该书的每一页(我都还没有做到这一点),但你们应当将这本书作为这门语言的终极参考,因为它的确是字字珠玑。
本书中包含有相当多的模板代码(哪一本现代C++(www.cppentry.com)书籍不是如此呢),但我并没有假设你是名大师级的人物, 或掌握了高阶元编程(www.cppentry.com)知识。话虽如此,我还是建议你们最好熟悉如何使用模板,比如组成C++(www.cppentry.com)标准库中最流行的部分的那些内容。我努力将对模板的使用控制在一个合理的水平,但我们必须得承认,正是对模板的支持使得C++(www.cppentry.com)具有"自我修复"的能力,而这也是本书得以诞生的主要原因。
由于灵活性和实用性对我而言非常重要,因此书中的代码并非只能在少数最新编译器上编译;实际上,书中几乎所有代码都可以在任意一款"还算可以"的现代编译器(见附录A)上编译。当然,有一些很好的编译器是可以免费获得的,并且你也可以相信你的编译器能够编译这些代码。
只要有可能,我就会尽量避免涉及特定的操作环境、库和技术。然而我也略微谈到了一些,所以,了解以下内容将会有所帮助(尽管并非必不可少):COM和CORBA、动态库(UNIX和Win32的)、STL、线程(POSIX和Win32的)、UNIX以及Win32。参考书目中包含有许多这些方面以及其他方面内容的好书。此外,熟悉不止一种机器架构也是有帮助的,当然这同样并非不可或缺。
由于C目前仍是语言间交互以及操作系统开发的通用语言,因此它继续作为一门极其重要的语言存在着。尽管本书是关于C++(www.cppentry.com)的,但在某些领域,C跟C++(www.cppentry.com)之间的共性会变得很重要,我认为在这些领域选择兼顾C/C++(www.cppentry.com)是合理的。事实上,正如我们将在本书的第二部分中看到的,我们有时需要求助于C来支持C++(www.cppentry.com)的一些高级用法。
此外,我还作了一个重要的假定。我假定你们也认为工作的质量是非常重要的,并且有动力去寻找达到这一目标的新途径。本书不敢妄称是这些所谓新途径的惟一源泉。确切地说,它提供了一种实践性的、有时甚至是异端的视角来看待我们在C++(www.cppentry.com)中遇到的问题。如果情况够好的话,本书或许也能成为你的精华书库中的成员之一。最终的责任落在你自己的肩上,剩下来要做的就是去寻找最好的工具来支持你的工作。
本书的组织方式
本书的主要内容分为6个部分,每一部分由一个绪论和5~7章组成,每一章又可以被进一步划分为若干节。
既然本书取名为"Imperfect C++(www.cppentry.com)",那么我就会在书中尽量突显出实际的不完美之处,这就是你们在本书的通篇都会发现一些所谓的"不完美(Imperfection)"的原因。在书的前几部分,这些不完美之处出现得比较密集,这说明它们自身和它们的解决之道尚且是比较简单的。每个小节都对应着语言的某个特定的特性,并且通常会介绍它的一个不完美之处。只要有可能,我就会提供一些具体的技巧或技术来解决这些问题,或至少为开发者提供控制问题的方法。随着本书内容逐步展开,这些不完美之处将会变得不再像前面那样散碎,显得更为重大,从而伴随有更大篇幅、更加详细的讨论。
本书并没有采用时髦的"自助餐"式的写法,也不存在一条必须从头读到尾的贯穿全书的主线。当然,大部分后续章节的内容是根据前面章节的内容进行描述的,有时甚至建立在之前的章节之上,所以除非你故意作对,否则最好还是按顺序阅读。然而,一旦你读完一遍后,回头再来参考其中的某些部分时,你就可以根据需要跳至任意一处而不再需要通读所有内容了。在每一章中,各节一般是按内容顺序排列的,所以我建议你也应该按顺序来阅读每一节。
在难度方面,第一部分到第四部分显然经历了一个从易到难的过程,从相当简单明了直到有相当高的要求。 尽管第五部分和第六部分需要依赖第三部分和第四部分的一些内容,但它们相对来说不再那么具有挑战性了。你可以优哉游哉地一直读到附录。
主要内容介绍完之后是4个简短的附录。附录A介绍在探讨本书各项议题时使用的编译器和库的详细信息。附录B向你展示一个年轻的C++(www.cppentry.com)工程师在刚踏入这一充满陷阱的领域时所犯下的一些令人目瞪口呆的错误。附录C介绍Arturius 项目,这是一个免费的源码开放的编译器多路分发器,你也可以在随书光盘中找到它。最后,附录D介绍随书光盘的内容。
我的编码风格十分一致,甚至可以说是严格,你也可以说它过于"学究气",我以前的同事和使用我库的人们早就这样说过。但我之所以采取这种风格是因为这么一来代码中的所有东西都有其明确的位置,人家不至于会问"某某东西到哪里去了"这种问题,同时这也意味着当我几年后重新面对这些代码时,我还可以轻松搞定它。当然这么做也有缺点:我需要一个21英寸的显示器和一个工业用激光打印机。
为了尽量减少我的编码风格对阅读本书的影响,我在书中出现的代码示例中使用了一些自由风格。你将会在示例中看到很多省略号(...),它们一般表示前面的有关例子中已经包含了该段代码,或者表示这些省却的代码是我们司空见惯的样板式代码(例如,禁止客户代码访问某些方法,见2.2节)。只有编码风格中对可靠性有着显著影响的方面才会被加以讨论(见第17章)。
参考资料
我在阅读其他C++(www.cppentry.com)书籍时感到不满的一件事是:作者指出事实的同时从不提及标准的相关部分。所以我在写作本书的过程中,除了会不断引用一些相关的书籍和文章外,还会在描述C++(www.cppentry.com)语言行为的同时提供其在C++(www.cppentry.com)(C++(www.cppentry.com)98)或C(C99)标准中的相关参考。
补充材料
光盘
随书光盘中包含有一些库、编译器(包括书中描述的大量编码技术)、测试程序、工具,以及其他有用的软件,另外还有许多摘自各种出版物的相关文章。关于光盘的详细内容,请参考附录D。
网上资源
你也可以通过如下网址来获取补充资源:http://imperfectcplusplus.com。
致谢
在几乎所有你看到或将要看到的书籍当中,你都会发现其中有一页或几页充满着对家人和朋友热情洋溢的致谢之辞,我可以向你保证这些言辞都是发自内心的真情流露。一本书要想得以完成,其背后必定隐藏着若干人的支持。
首先感谢我的母亲和Suzanne,感谢她们长期以来的容忍、支持以及养育之恩,直到当年的唧唧喳喳的小布谷鸟到了早就成熟的年龄(27岁),才离开温暖的巢,飞向一个不同的世界。感谢母亲和Robert在一段艰苦但最终有所收获的岁月里一直帮助小布谷鸟和他的家庭。尤其感谢Robert在这段时间里的许多重要时刻帮我保持精神放松。
谢谢Piker填补了这个大家庭的空缺,并且不遗余力地照看孩子,鼓励我们,并提供免费的午餐。同样感谢Dazzle,他总是告诉我他对我极有信心,并难能可贵地放弃那些诱人的DBA大师活动,坚定地帮我做那些沉闷的审稿工作;他在看其他Perl和Python脚本的时候可决不会是这个心态!也要感谢Besso一直以来对我的计划的浓厚兴趣、过分的自豪以及鼓舞人心的观点。谢谢Al和Cynth(亲家)提供许多免费的饭菜和美味的巧克力(哦,我差点忘了我那辆自行车……)。
衷心感谢808 State、Aim、Barry While、Billy Bragg、De La Soul、Fatboy Slim、George Michael、Level 42、Rush、Seal、Stevie Wonder以及The Brand New Heavies,没有他们我不可能从这个一年半的幻想中挣扎过来。
最重要的感谢要给予我的美丽爱妻Sarah,感谢她抑制住合乎情理的担心和疑虑,而表现出完全的支持和信任。她是我心目中真正的明星!
感谢一些出色的人,他们对我的教育和事业的影响是深远的。感谢Bob Cryan教授,感谢他能够赏识一名天赋不错的学生,并在后来3年的研究生在读期间宽容他的逃课(去骑自行车)。
还要感谢Richard McCormack让我保持在概念上的优雅之外还看到了代码的高效之美。这段时间我有时会被别人责备说过于看重效率了,我就说你要怪就怪Richard去吧。另外,感谢Graham Jones(绰号"Dukey")教我设置vi,在那疯狂的6个月的时间里和我保持深厚的友谊以及提供开心的玩笑。这些东西,无可替代!
同样还要感谢Leigh和Scott Perry,感谢他们向我介绍"螺栓"概念以及其他优秀的技术。
特别感谢Andy Thurling。在我怀揣博士文凭和名不副实的软件工程技能等级证书去找工作时,Andy对我的潜能表现出充分的信心。 Andy还教给我或许在这个奇妙而令人畏惧的职业中最最重要的一课:"我们只是在尽量充分利用手头拥有的信息而已(Skegging it Out)"。 Chuck Allison则以更为亲切的形式来表述这个意思,那是一句来自古老的印第安人部落中的箴言:"向一个不断学习的人学知识就好比是在饮一条生生不息的河流"。
任何书籍的成功都离不开出版社、评审者以及其他给出指导和建议的人们的帮助。感谢我的编辑Peter Gordon给予鼓励并包容一个热情而任性的作者在写他的第一本书的过程中情绪起落。同样感谢Peter的得力助手Bernard Gaffney,他很好地控制了整个计划进程,并耐心地答复我每天发去的几封电子邮件。还要感谢Addison Wesley出版社的产品和市场部门的其他职员,包括Amy Fleischer、Chanda Leary-Coutu、Heather Mullane、Jacquelyn Doucette、Jennifer Andrews、Kim Boedigheimer以及Kristy Hart。衷心感谢(并致歉)我的项目经理Jessica Balch不厌其烦地帮助我纠正书中糟糕的句法、蹩脚的幽默以及英式英语的拼写(例如有很多"ize"被写成了"ise")。还要特别感谢Debbie Lafferty,2002年的某一个晚上我在睡梦中冒出了"Imperfect C++(www.cppentry.com)"这个念头,是Debbie Lafferty鼓励我付诸实现的。
感谢忠诚的评审者们,他们是Chuck Allison、Dave Brooks、Darren Lynch、Duane Yates、Eugene Gershnik、Gary Pennington、George Frasier、Greg Peet、John Torjo、Scott Patterson以及Walter Bright。没有他们我肯定会到处磕磕碰碰,踉踉跄跄。他们中的一些人让我保持心情愉快,有些人则使我质疑这个写作计划是否明智,但无论如何他们的反馈信息都极大地有助于我改善最终的成果。我们生活在一个奇妙的世界中,来自各个不同国家的人们可以建立起友谊,虽然其中大部分人我未曾谋面,却能够给予我如此之多的帮助,这真是奇妙!
同样要感谢Addison Wesley的审阅者们,包括Dan Saks、JC van Winkel、Jay Roy、Ron McCarty、Justin Shaw、Nevin Liber以及Steve Clamage。他们的反馈信息对于本书的篇幅能够降到1000页以下起到了至关重要的作用,而且帮助我避免了一些无聊的闲话和粗心大意的错误。作为一名作者,我早已领略了写作和审稿过程的艰辛,我知道一次彻底的审阅需要付出多少精力,真的非常感谢他们投入的时间和精力。
感谢Peter Dimov允许我引用他的名言(在第26章),同时还对第五部分各章给出了极好的建议。感谢Kevlin Henney对第19章和智能强制的一些有趣的讨论所给予的关注。感谢Joe Goodman仔细审阅有关C++(www.cppentry.com) ABI的那一部分(第7、8两章),使我最终能够呈上一份像样的讨论。感谢Thorsten Ottosen在第1章有关契约式设计部分提供的类似的帮助。
特别感谢Chuck Allison、Herb Sutter、Joe Casad、John Dorsey以及Jon Erickson,他们在过去的几年中在各方面都给予我极大的支持和鼓励。
感谢Bjarne Stroustrup的鼓励,他还不时为我补上一点历史常识。哦,首先还是要感谢他发明了这门神奇的语言!
感谢Walter Bright在本书的写作过程中持续不断地改进他的优秀的Digital Mars C/C++(www.cppentry.com)编译器,在世界上最缺乏耐心的家伙持续的质问面前保持责任心,而且还友好地将我引领入D语言开发世界,后者也给我这本书的写作带来了不少灵感。同样感谢Greg Comeau,尽管他用不着对他的Comeau编译器做那么多的改进:Comeau是目前业界最遵从标准的C++(www.cppentry.com)编译器!他们除了是优秀而又反应迅速的编译器供应者之外,还在众多问题上持续不断地给予鼓励、建议以及其他信息。
感谢CMP出版公司允许我在书中使用我原先在他们刊物上发表的文章中的一些内容,并允许我将原先的几篇文章放在随书光盘中(见附录D)。
感谢Borland、CodePlay、Digital Mars、Intel、Metrowerks以及Microsoft公司为我的研究、写作以及编写开源库提供编译器。
特别要感谢Digital Mars、Intel和Watcom公司授权我将他们的编译器放在随书光盘中(见附录D)。另外,Greg Peet在光盘的设计及内容方面的巨大帮助值得大大嘉赏。
谢谢C/C++(www.cppentry.com) User's Journal、Dr. Dobb's Journal、BYTE以及Windows Developer Network的读者,他们为我的文章和专栏友好地提供了反馈信息和支持。
同样感谢整个C++(www.cppentry.com)社群,以及那些关注各种与C++(www.cppentry.com)有关的新闻组(见附录A)的好心人。他们是Attila Feher、Carl Young、Daniel Spangenberg、Eelis van der Weegen、Gabriel Dos Reis、Igor Tandetnik、John Potter、Massimiliano Alberti、Michal Necasek、Richard Smith、Ron Crane、Steven Keuchel、Thomas Richter以及"tom_usenet",而且或许还有好些人没有列出。尤其感谢Ilya Minkov,是他要求我为STLSoft库增加属性(Properties)实现的,而我自己以前可没有产生过这个念头。如果不是因为这个建议,我可能永远也不会实现这项我钟爱的技术(见第35章)。
最后,还要感谢STLSoft库的所有用户,没有他们的信息反馈,这个库中的许多特性就不会存在,而且本书中的某些部分也会变得难弄得多。
Matthew Wilson