《Thinking in C++》及《Thinking in Java》的做者Bruce Eckel向来是个“拥C++反Java”派,他曾经不止一次的提到,C++语言特性的添加有多么的深思熟虑,而Java又是如何的把一些奇怪的东西不停的加进去。Bruce认为,理解语言特性为何会存在是很是有帮助的。他将其称之为“语言考古学”。c++
在C++委员会会议上我所能找到的,是C++社区里最聪明的一群人,群英荟萃,为我答疑解惑。我很快意识到,这种方式之好,远超我在任何一门研究生课程中之所得。若是考虑到研究生的机会成本,这仍是一笔在财务上要划算得多的生意。程序员
我被深深吸引住了,坚持出席了有大约8年的时间。在我走后,委员会仍继续前行;虽标准仍未制定完成,但彼时Java已经出现了,还有一些其余(语言)的草案也问世了(这是技术刺激成瘾者的毛病——个人确钻研某一门语言,但我也一直在寻找更有生产力的手段:那些前景看起来很光明的语言特性能够绝不费力地分散个人注意力)。编程
每次你们见面的时候,我都会抛出一列清单,这是我累积下来的有关C++的棘手问题列表。一般我会请他们在几日内予以澄清。出席委员会能看到的最有价值的东西就是这个,固然,还包括得以早早接触到即将公布的新特性。数组
从长远来看,把语言特性添加进C++的谜团里面并观察它,是一门深奥的学问。如今说三道四是一件很简单的事情,说什么C++太烂了,设计太糟糕了等等。在对C++设计时所受的约束都没有任何理解时,不少人就这样脱口而出了。Stroustrup(51CTO编者注:这个Stroustrup也就是邀请做者参会的Stroustrup,也就是C++语言的设计师Stroustrup)的约束是,C程序应该稍做改动,或者最好不作改动,就能在C++下编译。且无论这是否是彻底合乎逻辑,但它给C程序员提供了一个很好的演进路径。不过这存在较大的局限性,须要把每一项你们抱怨不已的困难特性都一一虚拟化。因为这些特性难以理解,许多人就直接得出结论说C++设计糟糕,而这远非事实。安全
在语言设计上,Java用傲慢的态度对待这一认识。关于这一点,我在《Java编程思想》及许多博文上都写过了。所以个人长期追随者都知道,因为Gosling(Java语言之父)和Java语言设计者对C++的否认态度,Java一开始就把我拧到了错误的方向。说实话,我与Gosling 的首次“邂逅”印象糟糕——那是不少年之前的事了,当时我刚进入第一家公司,第一次开始使用UNIX (Fluke,生产电子测试设备;我在里面作嵌入式系统编程)。有一位软件工程师辅导我,教我使用emacs。不过当时公司里惟一的工具只有Gosling Emacs的商用版(Unipress)。若是你作错了什么,程序会侮辱你,把你叫作火鸡,并把屏幕填满垃圾。这样的东西出如今了一个商用产品上,而咱们公司但是花了至关一笔钱的。不消说,等到Gnu emacs变得稳定起来后,公司立刻就换到了Gnu emacs上(我见过Richard Stallman。固然,他是个疯狂的家伙。不过他也是绝顶聪明的:他知道当遇到麻烦的时候,你须要的是帮助,而不是侮辱)。(51CTO编者注:Richard Stallman即Gnu emacs的开发人员,美国一位著名黑客。闭包
我不知道对Gosling印象的这段造成经历在多大程度上影响了我后面对他工做的见解,但事实上,“咱们看见它太差劲了,就决定拿出本身的语言”,对C++的这种态度于事无补。尤为是当我开始在《Java编程思想》的写做过程当中把它弄清楚,并多次发现,那些草率决定的语言特性与库,都不得不予以修订——确实如此,其中的大部分都必需要修订,有些修订仍是在程序员已经忍受了多年以后才落实。在许多场合下,Gosling坦诚他们必须马不停蹄,不然就要被互联网革命超越了。编程语言
我发现,理解语言特性为何会存在是很是有帮助的。若是是由大学教授一会儿和盘托出,把它们端到你面前,你势必就会构想出这门语言的一个神话,说“这种语言特性之因此存在,确定有一些真正重要的缘由,这些缘由只有建立这门语言的聪明人才能理解,我是理解不了的,我信赖它就是了”。从某方面来讲,对语言特性这种基于信仰的接受是一种负担;它阻止你对所发生的事情进行分析和理解。在个人主旨演讲中(Bruce将在将来几天参与一个主旨演讲),我会关注一些特性,并检查一下它们在不一样语言中是如何被实现的,以及为何被实现。函数
这里就有个例子。对象建立。在C语言中,声明了变量以后编译器就会为你建立堆栈空间(未经初始化,除非你初始化,不然会有垃圾数据)。可是若是你想要动态地作这件事情,你就得使用 malloc() 和 free()这两个标准库函数,还要当心翼翼地手工执行完全部的初始化及清理工做。若是你忘了,就会出现内存泄漏等相似灾难,这是常有的事。工具
有关动态对象建立:通常来讲,编译器将内存分为三部分:静态存储区域、栈、堆。静态存储区主要保存全局变量和静态变量,栈存储调用函数相关的变量、地址等,堆存储动态生成的变量,在c中是指由malloc,free运算产生释放的存储空间,在c++中就是指new和delete运算符做用的存储区域。学习
由于malloc() 和 free() “仅仅”是库函数,在基本编程课上,应有的相关知识一般没有被传授,使人既迷惑不解又胆颤心惊。当程序员须要分配大量的内存空间时,他们就不去学如何来使用这些函数进行处理,取而代之的是经常就分配一个巨型数组的全局变量了事(不是开玩笑),数组之大,远远超过他们曾自认为所需的空间。程序彷佛工做了,再说了,好像谁都不会用到产生越界——所以,当多年以后它的确发生的时候,程序中断了,而某个可怜的家伙就得一头钻进去,把错误在哪里这个谜底给找出来。
Stroustrup认为动态分配须要更简单、更安全——这一块得放到语言核心中,而不是降格为库函数。还必需要与初始化和清理一块儿协同工做,初始化和清理必须由构造函数和析构函数分别提供,以便为全部对象提供相同的保证。
这个问题是影响了所有C++决策的一块里程碑:对C的向后兼容性。理想状况下,对象的堆栈(heap)分配可只需忽略便可。但C的兼容性要求进行堆栈(stack)分配,所以必须对heap对象和stack对象进行区分。为了解决这个问题,C++从SmallTalk挪用了new 这个关键字。建立 stack 对象只需声明便可,像这样: Cat x;或者带参数的状况下, Cat x("mittens");。而建立heap 对象时,就使用new,像这样: new Cat x; 或者 new Cat x("mittens");。利用这个约束,咱们获得一个优雅而一致的解决方案。
自从断定C++的一切都作得很差且过于复杂以后,Java就产生了。具备讽刺意味的是,Java 决定把 stack 完全抛弃了(特别是忽略了基本类型上的失败,这点我已经在别的地方指出过了)。真好啊,既然全部对象都是在heap上分配的,区分stack和heap的分配就没有必要了。他们能够轻易说 Cat x = Cat() 或者 Cat x = Cat("mittens")。或者甚至更好地,用联合的类型引用来消除重复(不过那样——还有像闭包(closure)之类的其余特性——就显得“太长”了。所以咱们反而离不开Java的平凡版;类型推导已经讨论过了,但我敢打赌那不会发生,也不应发生。由于这会在给Java增长特性的同时带来问题)。
Guido Van Rossum (Python的建立者)采用了一个最小化的方案——常常为人所痛斥的空白的使用,正说明了他对语言简洁性的追求。既然 new 关键字再也不必要,他就省去了,好像这样: x = Cat("mittens")。Ruby 也可能用了这种方法,不过Ruby其中一个主要的约束是尽量追随Smalltalk,所以在Ruby是这样的:x = Cat.new("mittens") 。但Java以贬低C++作事的方式为准则,以致于用了new 这个关键字成了一个迷了。自研究了该语言在其余地方所作的决策后,个人猜想是,他们是否是历来就没有意识到,这东西根本就是无关紧要的?
所以这就是我所说的语言考古学的意思。我但愿人们能用一个更好的视角来看待语言设计,并在学习一门编程语言时,能有更多的批判性思惟过程。