作者介绍
Scott Meyers,C++顶级权威之一,为世界各地客户提供培训和咨询服务。出版有畅销的Effective C++系列图书(《Effective C++》、《More Effective C++》和《Effective STL》),设计了创新型的Effective C++ CD,Addison Wesley的Effective Software Development Series顾问编辑,The C++ Source (http://www.artima.com/cppsource/)咨询板块专家。布朗大学计算机科学博士,他的网站是www.aristeia.com。
在本系列的第三篇文章里,我将把视线转移到评选过去最重要的C++软件上来。
用C++编写软件,你需要工具的帮助。在我看来,这些工具曾经是(将来也是)有关C++的最重要软件。可以想见,曾经出现了不少用C++开发的重磅软件,它们促使很多人为了以后项目的开发选择了这门语言,但我不关心这些。这门语言最重要的软件应该是软件开发人群使用的最基本的东西:编译器和库。可能,C++是更为库编写而不是应用开发所设计的一门语言。
我选择的C++历史上最重要的五个软件如下,以诞生年份为序:
Cfront,AT&T的Bell实验室开发,1985-1993。Cfront是最早的C++编译器。它可是真正的编译器,不过生成的是C格式的目标码。因此将它认作C++到C代码的预处理器是很自然的。很难让人在调试时不做它想,因为至少在我1998年开始使用它时,仍然没有C++调试器。头发花白的前辈们当时使用C调试器,必须要对付那些让人精神崩溃的名字(比如,识别调试器里指向C++源代码中某个加法函数的__pl__1Aff)。
事实上,Cfront生成C目标码有两个好处。第一,可以非常容易将Cfront移植到新的平台,因为C编译器到处都是。这就促成了C++在各种环境里的快速传播。第二,使用者可以观察编译生成的代码,知道编译所做的工作。当大多数人还是C++新手的时候,通过揭示其工作过程,有助于消除大家对C++的神秘感。它也扮演某种保护伞的角色。不用担心C++用黑纱蒙住你的眼睛,因为它所做的任何事情都清清楚楚摆在那里(至少和从前机器生成的C代码一样清楚)。
在1990年前,Cfront不仅是个编译器,而且也成了事实上的语言标准。C++诞生于AT&T,Cfront来自AT&T的C++小组,因此无论Cfront干什么,自然是不会错的。长期以来,其他厂商的编译器紧跟Cfront,以致Cfront的bug都被原样复制。直到《Annotated C++ Reference Manual》(ARM)发布,Cfront的标准色彩才逐渐消退,特别是大家认识到要为Cfront加入异常处理机制需要付出巨大努力的时候。
Cfront的最后版本发布于1993年,但阴魂不散。Edison Design Group,一家专业生产最贴近C++标准的front-end编译器的商业公司,在2006年7月的文件里还指出它们的编译器兼容Cfront模式。我猜测仍然Cfront仍然在一些不支持本地C++编译器的嵌入式项目中发挥作用,当然仅仅是猜测。
GCC,GNU工程的杰作,1987至今。GNU很早就进入了C++商业领域,并且发布了第一个生成本地代码的编译器(相对Cfront的C++-C转换而言)。多年以来,GNU编译器成了跨平台应用开发的不二选择。事实上,它是一个交叉编译器,这也使它在嵌入式系统开发领域广受欢迎。GCC本身是一个支持多种front-end(包括C、C++和FORTRAN等)和针对各种平台的back-end工具的编译器平台,其C++版本就是广为人之的g++。
g++的早期推动人是Michael Tiemann。我不由得回忆起自己曾经提交了一份g++的bug报告,很快就得到了Tiemann的回应。他提供了新的g++文法,并请我据此重建编译器,看看我报告的问题是否解决了。我记得问题依然存在,但一个编译器的作者特意送你一个修改补丁并请安装试用,已经难能可贵了。顺便提及一下,Tiemann于1989年与人合伙创办了Cygnus Support,我相信Cygnus是历史上第一个提供免费软件的公司。据说早期的时候,Tiemann有时会躺在浴盆里召开Cygnus的会议。
g++是开源的,这样C++社区就可免费获得与C++标准一致的 front-end工具。但我从未听说哪个以g++代码为基础编写的开源工具(如Lint、重构工具等等)的解析能力能与g++比肩。有不少可以解析所有C++声明的工具(比如gccxml),但据我所知,没有哪个工具能同时解析声明和定义部分(特别是函数体)。因此尽管我于它没有个人使用经历,但我怀疑g++的front-end是否在完全开源上有所保留。这对于C++开发者来说是不幸的,因为尽管有很多工具可供使用,但其真正威力应该是解析[注释1]C++源代码的能力,这是一个难以逾越的障碍。
Visual C++,Microsoft出品,1992至今。VC++是C++成功的最大推动力之一,也是延缓C++进步的最大障碍之一。尽管Bjarne Stroustrup断言“没有人知道大多数C++开发者到底在干什么”[注释2],但我基本上不会怀疑如果我将这颗星球上所有C++开发者召集到一起,并要求为他们使用的编译器投票,我想大多数人都会提到VC++。仅就这点而言,VC++已经并将继续对C++产生重要影响。而且,Microsoft的旗舰产品(如操作系统、Office等)也完全或主要用C++编写,并用VC++编译。这也加重了VC++在C++世界的分量。该公司对C++的倚重促使它开发了很多工具和API来支持其应用,从而导致很多Windows开发人员也使用这门语言。到了90年代晚期,很多人(这让我震惊甚至恐惧)将C++叫做“Microsoft语言”[注释3]。VC++在C++领域占据了统治地位。对于大多数程序员而言,Visual C++就是C++。
不幸的是在Microsoft的C++实现里,标准的缺失持续了很多年。1998年前,这算不上一个问题,因为根本就不存在可以遵从的标准,其他编译器厂商也是各行其道。然而,VC6于1998年发布,此时距C++标准完成已近一年。与同类产品相比,VC6与C++标准差得最远。当然这在当时仍然不是一个十分的严重问题,因为很少有程序员马上使用C++标准里的最新特性。糟糕的是VC6一直维持到2002年(VC7发布),甚至VC7编译器本身也没有大的改变。在这些年里,对标准的支持,基本上都是以更新库(如STL)的形式实现,而我们很多人都觉得多年前就应该发布升级版本。2003年的VC7.1终于解决了大部分标准兼容问题,但在VC6至VC7.1的六年间,使用VC6的跨平台开发者必须因其缺陷付出艰苦努力,最典型的就是通过条件编译(也就意味着代码零乱)解决模板偏特化等问题。不过,很多开发者直到今天仍在使用VC6,他们也得继续感谢VC6小组大范围忽视标准兼容的决定。比如,新的版本在内存耗尽时不抛出异常,而直接返回空指针。
2003年后,VC++不再存在我认为相当严重的标准兼容性问题,我相信它现在的领导者非常重视标准兼容。然而,我在1998到2003这六年来经常接触的都是工作(至少就编程序而言)中并无特别困难的开发者,因此我理直气壮地决定将我对Microsoft的“警惕”保持到2008年——从他们发布一个“真正的”C++编译器起的六年。
The Standard Template Library,源自HP,1993年至今。像C++这类语言的标准库应该提供一系列常用容器和算法,这个说法并不会让人格外惊讶,但即便这样的作品也可能上这个榜。而STL对C++的贡献远非如此。它引入了容器和算法的设计架构,这样,仅仅通过迭代器交互就能实现无缝协作。它示范了如何用容器和迭代器替代数组和指针。此外,它还告诉用户如何扩展架构,允许引入新的容器、算法和迭代类型——它们可以像STL自有组件那样和任何符合STL标准的组件一起工作。所有这些都以效率为实现基础,不考虑经典的面向对象、基于继承的解决方案,而几乎完全依赖于模板技术。它还为C++引入了泛型编程。这一切的努力给我们带来了库和库框架设计思路上的转变,也满足了那些寻找便利的、可移植容器者的需求。我在《Effective STL》(Addison-Wesley, 2001)里写到,“我已经编程三十年,但我从未发现STL的比肩者”。现在已经超过了30年,我仍然没有发现。
Boost类库,诞生于1999年。这项选择似乎带点儿欺骗,因为Boost实际上不是一个类库,而是一个收容人们因各种目的和想法而设计实现的一系列库的组织。因此,Boost库在提供什么以及如何提供功能上似乎漫无目的。然而,Boost及其库对C++产生了重大影响,这种影响在未来可能更为明显。TR1中规定的14类新功能(其中13个已经写入C++0x草案)里,有10个[注释4]来源于Boost库使用者的建议。
Boost的影响并非偶然。它就是以充当那些可能最终被加入C++标准的库的实验床和推动者为目的而创立的。TR1对Boost的重视已经证明了它的成功,而且TR2很可能包纳Boost中更多功能。
如果在Google中搜索“C++ libraries”(不带引号),你会发现Boost排在头条。这是否意味着人们想到C++库,就会想到Boost呢?我想它至少说明了Boost与C++库的关系是多么密切吧。
当前,软件工具已经成为C++成功的关键因素,但归根结底,任何美好的东西都要人来创造。C++是如此,人类任何别的劳动也是如此。在下篇系列文章里,我将告诉你我认为谁是最重要的人——C++世界的巨人[注释5]。
注释:
1.所谓“解析”,我指的不仅仅是构建抽象语法树,还包括执行隐式模板实例化、解析重载函数调用等。也就是说包括解析和语义分析,不过很多C++工具的威力起点是语义分析的结果。人们常常将这整个过程叫做“解析”。
2.C++社区是如此庞大和复杂,以至于无法分析“大多数程序员”的行为。
3.“Unix语言”是Java,我不知道Apple语言应该是什么。
4. 它们是:reference wrappers、smart pointers、enhanced member pointer adapters (mem_fn)、enhanced binders (bind)、generalized Functors (function)、type traits、random numbers、tuples、fixed-size arrays和regular expressions。
5.每个人都知道巨人的力量,但我做名人研究的老婆却提醒我说,尽管巨人们都力量超群,但在他们的漫漫长路上,更多依靠的是艰辛努力而不是他们天生的神力。这就是他们前仆后继投身与奥林匹斯山主宰者(宙斯和他的仆从)斗争的原因。我将把判断C++世界里是否存在同样现象的问题留给你们,如果有,那么是谁或者说是什么扮演着奥运会选手的角色呢?