前言
一本书之所以成功,不在于书本身的内容,而在于它的未尽之言。
--马克·吐温
尽可能简单,但不过分简单。
--阿尔伯特·爱因斯坦
一个对读者的能力持怀疑态度的作家根本不能称其为作家,只不过是个阴谋家而已。
--E. B. 怀特
当Herb Sutter接手C++(www.cppentry.com) Report的编辑工作时,他很快就邀我为之写一个专栏,主题由我来定。我将该专栏命名为"Common Knowledge"(常识)。用Herb的话来说,该专栏预期为"对每一位职业C++(www.cppentry.com)程序员应该知道但未必知道的基础知识之定期概述"。然而,在以那样的风格写了一些专栏文章后,我对模板元编程(www.cppentry.com)(template metaprogramming)技术的兴趣日渐浓厚,故而此后"Common Knowledge"中讨论的一些主题距离"Common"越来越远。
然而,在C++(www.cppentry.com)程序设计界,当初促使我选定写这个专栏的问题仍然存在。在我的培训和咨询工作中,常常会遇到下面几类人员:
领域专家,他们是专家级的C程序员,但对C++(www.cppentry.com)只有一些基本的认知(并可能对 C++(www.cppentry.com) 没有好感);
直接从大学雇来的新手,他们有才干,但对C++(www.cppentry.com)语言只有理论上的认识,缺乏实际产品开发经验;
专家级的Java程序员,他们仅有少量的C++(www.cppentry.com)经验,常会以Java的方式来从事C++(www.cppentry.com) 编程(www.cppentry.com);
C++(www.cppentry.com)程序员,他们具有若干年维护现有C++(www.cppentry.com)应用程序的经验,但尚未经受学习高级编程(www.cppentry.com)知识的挑战。
我希望能即刻进行建设性的工作,但是,许多我共事过的或培训过的人都需要先接受形形色色的关于C++(www.cppentry.com)语言特性、模式以及编程(www.cppentry.com)技术的预备性的教育,才有能力处理业务问题。更糟糕的是,我怀疑大多数C++(www.cppentry.com)代码在编写时都至少忽略了一些基本要素,因而不具备大多数C++(www.cppentry.com)专家所认可的产品级的质量。
这本书致力于解决这个具有普遍性的问题,它提供了每一位职业C++(www.cppentry.com)程序员需要知道的常识,并且这些常识都被精简至本质,因此可被高效而精确地吸收。其中有不少信息也可以从其他途径获得,而有些知识则是所有专家级C++(www.cppentry.com)程序员知道但未成文信息的完整摘要。本书优势在于,这些材料现在被集中于一处,并依据我多年的培训和咨询经验进行了遴选,经验表明,这些都是最常被误解同时也是最有用的语言特性、概念和技术。
也许构成本书的63个简短条款最重要的方面在于它们所省略掉的东西,而不是它们所包含的东西。许多主题都可以进行更复杂的讨论。如果忽略掉这些复杂性,会导致传达的信息不够充分,从而可能会误导读者,但对一个主题的全部复杂性进行专家级的讨论,又可能会使读者应接不暇。本书采取的方式是在讨论每一个主题时过滤掉那些"不必要"的复杂性。我希望有幸留下的这些东西是对产品级C++(www.cppentry.com)编程(www.cppentry.com)所必需的知识的清晰萃取。较真的C++(www.cppentry.com)语言专家可能意识到我没有讨论某些有趣的甚至重要的问题(从理论的角度来说),但我所省略的那些东西通常并不会影响阅读和编写产品级C++(www.cppentry.com)代码的能力。
写作这本书的另一个动机来自于我在一次会议上同一群知名C++(www.cppentry.com)专家的谈话。这些专家对于一件事情颇感沮丧,那就是他们认为现代C++(www.cppentry.com)是如此复杂,以至于"普通"程序员已经不再能够理解它了(比如,在模板和名字空间上下文中的名字绑定问题。是的,解决这样的问题确实需要普通C++(www.cppentry.com)程序员下更多的功夫)。在我看来,应该说其实我们的态度有些过于自负了,我们的沮丧也是不合情理的。我们这些"专家"们自己就不存在这样的问题,实际上,使用C++(www.cppentry.com)编程(www.cppentry.com)就像说一门(远比C++(www.cppentry.com)复杂的)自然语言那样容易,尽管我们不能完全分析我们所说的每一句话的语法结构。本书不断出现的一个主题是,虽然对特定语言特性细节的完整描述可能让人望而生畏,但是,日常使用的语言特性都是直观而自然的。
不妨考虑一下函数重载。有关它的完整描述占据了很大一块标准文档,并占据了许多C++(www.cppentry.com)教程的一整章(甚至多章)。然而,当我们面对如下代码时
- void f( int );
- void f( const char * );
- //...
- f( "Hello" );
一个职业C++(www.cppentry.com)程序员是不可能不知道哪一个f被调用的。有关重载函数调用解析规则的完整知识当然是有意义的,但很少会用到。同样的道理适用于其他许多看上去很复杂的C++(www.cppentry.com)语言特性和惯用法。
这并不是说本书中出现的所有内容都很简单,它们"尽可能简单,但不过分简单"。在C++(www.cppentry.com)编程(www.cppentry.com)中,以及任何其他值得从事的智力活动中,许多重要的细节都无法写在"索引卡片"上。此外,这并不是一本"傻瓜"书。我感觉自己对那些挤出宝贵时间来阅读我的书的读者负有极大的责任。我尊重这些读者,并且努力与他们交流,就像我亲自与同事交流一样。我认为给职业人员写初中水平的东西算不上写作,不过是想低就迎合而已。
本书中的许多条款针对的是一些简单的误解,这些误解都是我曾一再看见的,只要予以指正即可,例如成员函数查找的作用域顺序、重写(override)和重载(overload)之间的区别等。另外一些条款则论述那些逐渐为职业C++(www.cppentry.com)程序员所必需、但常常又被错误地认为过于困难并因而被避免使用的知识,例如类模板局部特化(template partial specialization)和模板的模板参数(template template parameter)等。为此我受到了一些专家级审稿人的批评,说我在模板问题上花费的篇幅过多(约占全书的1/3),而这些知识并非真的是"常识"。然而,这其中每一位专家又都指出有一两个甚至好几个模板主题应该包含于本书之中。一个有趣的现象是,这些建议中几乎没有重叠,每一个和模板有关的条款都至少有一个支持者。
这就是构成本书所含条款的问题症结。我并不认为有哪一位读者对本书每一个条款所谈的主题都一无所知,而且我还认为甚至有人熟悉本书中的所有条款。显然,如果某一位读者对某个特定的主题不熟悉,我认为阅读本书应该会从中受益。然而,即使某一位读者已经熟悉某个主题,我还是希望他能从一个全新的角度来阅读它,这样也许能够澄清一些轻微的误解或进一步加深对该主题的理解。这本书可能还有一个作用,那就是可以为富有经验的C++(www.cppentry.com)程序员节省宝贵的时间。那些能干的C++(www.cppentry.com)程序员常常发现他们一再被问及同样的问题,从而影响了他们自身的工作。下次他们可以说:"先读一读《C++(www.cppentry.com)必知必会》,再来和我讨论该问题。"这样能够为这些C++(www.cppentry.com)专家节省大量的时间,从而允许他们将自己的专家经验用在更复杂的问题上,而这些问题才是需要专家来解决的。
起先我试图将这63个条款分组到若干章中,那样显得更加整洁,但这些条款自己却似乎并不乐意。它们往往彼此簇拥在一起,有时道理很明显,有时则有点出乎意料。举个例子,与异常和资源管理有关的条款形成了相当自然的一个组;而"能力查询"、"指针比较的含义"、"虚构造函数与Prototype模式"、"Factory Method模式"以及"协变返回类型"这几个条款之间的关系紧密得有点出乎意料,因而最好被安排在一起;"指针算术"和"智能指针"放在一块儿,而不是和出现于本书较早部分的指针和数组方面的素材放在一起。因此,我不再去武断地将章式结构强加于这些自然的分组上,而是决定让各个条款自由结合起来。自然而然,很多条款涉及的主题之间存在相互关系,简单的线形顺序很难表达出这一点,因此条款中还频繁出现内部交叉引用。所以说,它们是一个簇拥但紧密连接的共同体。
尽管本书写作的主要指导思想是短小精悍,但对一个主题的讨论有时会包括一些辅助性的细节,尽管它们和眼前讨论的主题并不直接相关。对于该主题来说,这些细节并非必需,但读者由此可注意到特定程序或技术的存在。例如,在好几个条款中都出现的Heap模板例子可以让读者顺便了解到有用但很少被讨论到的STL堆算法,而对placement new的讨论则勾画出许多标准库组件所用到的复杂老练的内存管理技术基础。只要这么做看起来很自然,我就会利用机会,将辅助性话题的讨论混入某个特定的具名条款中。因此,条款40"RAII"包含了对构造函数和析构函数激活顺序的简短讨论,条款57"模板实参推导"讨论了用于特化类模板的辅助函数的使用,条款12"赋值和初始化并不相同"则混入了对计算性的构造函数的讨论。这本书的条款数目本来很容易翻倍,但是,就像这些簇拥的条款自身一样,辅助性话题和具体条款的相关性,使得该主题被放置于合适的上下文之中,并且有助于读者高效、精确地吸收所表达的知识。
我很不情愿地加进了几个不适合在本书中讨论的主题(要知道,本书的写作风格是条款简短)。特别是有关设计模式和标准模板库的设计方面的主题,看上去短得可笑,很不完整。它们的现身只是为了消除一些常见的误解,并强调这些主题的重要性,从而鼓励读者去学习该主题更多的东西。
就像团聚在一起度假的家庭成员交流各自的趣事一样,常用到的例子早已成为我们编程(www.cppentry.com)文化的一部分,因此Shape、String、Stack以及任何其他常见的"嫌犯"都一一露面。对这些基准例子达成共识,可以使我们的交流像使用设计模式那样高效。例如"设想我希望旋转(rotate)一个Shape,除了……之外",或者"当拼接两个String时……"这些常见的例子更适合于交流,可以避免费时的背景介绍,比如"你知道当你的兄弟被逮捕时的表现吗?呃,说来话长,前些天……"
有别于我以前写的书,本书试图避免对一些糟糕的编程(www.cppentry.com)实践以及对C++(www.cppentry.com)语言特性误用作出评判--那是其他一些书的目标,其中最好的一些书已被我列于"参考文献"之中(但我免不了会有一些说教的倾向,在本书中还是提到了一些糟糕的编程(www.cppentry.com)实践,虽然只是顺带一提)。一句话,本书的目的在于以尽可能高效的方式告诉读者产品级C++(www.cppentry.com)编程(www.cppentry.com)所必需的技术。
Stephen C. Dewhurst
2005年1月于马萨诸塞州卡沃尔