《Java編程思想》作者:C++不垃圾,只是Java很傲慢
原創(chuàng)【51CTO外電精選】本文節(jié)選自《Thinking in C++》及《Thinking in Java》作者Bruce Eckel的博文,文章寫在一次C++規(guī)范委員會(huì)例常會(huì)議之后,Bruce受C++設(shè)計(jì)師(常被稱為C++之父)Bjarne Stroustrup邀請(qǐng)而參與了這次會(huì)議,并寫下了參會(huì)感想如下(節(jié)選):
在C++委員會(huì)會(huì)議上我所能找到的,是C++社區(qū)里最聰明的一群人,群英薈萃,為我答疑解惑。我很快意識(shí)到,這種方式之好,遠(yuǎn)超我在任何一門研究生課程中之所得。如果考慮到研究生的機(jī)會(huì)成本,這還是一筆在財(cái)務(wù)上要?jiǎng)澦愕枚嗟纳狻?/P>
我被深深吸引住了,堅(jiān)持出席了有大約8年的時(shí)間。在我走后,委員會(huì)仍繼續(xù)前行;雖標(biāo)準(zhǔn)仍未制定完成,但彼時(shí)Java已經(jīng)出現(xiàn)了,還有一些其他(語(yǔ)言)的草案也問(wèn)世了(這是技術(shù)刺激成癮者的毛病——我的確鉆研某一門語(yǔ)言,但我也一直在尋找更有生產(chǎn)力的手段:那些前景看起來(lái)很光明的語(yǔ)言特性可以毫不費(fèi)力地分散我的注意力)。
每次大家見面的時(shí)候,我都會(huì)拋出一列清單,這是我累積下來(lái)的有關(guān)C++的棘手問(wèn)題列表。通常我會(huì)請(qǐng)他們?cè)趲兹諆?nèi)予以澄清。出席委員會(huì)能看到的最有價(jià)值的東西就是這個(gè),當(dāng)然,還包括得以早早接觸到即將公布的新特性。
從長(zhǎng)遠(yuǎn)來(lái)看,把語(yǔ)言特性添加進(jìn)C++的謎團(tuán)里面并觀察它,是一門深?yuàn)W的學(xué)問(wèn)?,F(xiàn)在說(shuō)三道四是一件很簡(jiǎn)單的事情,說(shuō)什么C++太爛了,設(shè)計(jì)太糟糕了等等。在對(duì)C++設(shè)計(jì)時(shí)所受的約束都沒(méi)有任何理解時(shí),很多人就這樣脫口而出了。Stroustrup(51CTO編者注:這個(gè)Stroustrup也就是邀請(qǐng)作者參會(huì)的Stroustrup,也就是C++語(yǔ)言的設(shè)計(jì)師Stroustrup)的約束是,C程序應(yīng)該稍作改動(dòng),或者***不做改動(dòng),就能在C++下編譯。且不管這是不是完全合乎邏輯,但它給C程序員提供了一個(gè)很好的演進(jìn)路徑。不過(guò)這存在較大的局限性,需要把每一項(xiàng)大家抱怨不已的困難特性都一一虛擬化。由于這些特性難以理解,許多人就直接得出結(jié)論說(shuō)C++設(shè)計(jì)糟糕,而這遠(yuǎn)非事實(shí)。
在語(yǔ)言設(shè)計(jì)上,Java用傲慢的態(tài)度對(duì)待這一認(rèn)識(shí)。關(guān)于這一點(diǎn),我在《Java編程思想》及許多博文上都寫過(guò)了。因此我的長(zhǎng)期追隨者都知道,由于Gosling(Java語(yǔ)言之父)和Java語(yǔ)言設(shè)計(jì)者對(duì)C++的否定態(tài)度,Java一開始就把我擰到了錯(cuò)誤的方向。說(shuō)實(shí)話,我與Gosling 的***“邂逅”印象糟糕——那是很多年以前的事了,當(dāng)時(shí)我剛進(jìn)入***家公司,***次開始使用UNIX (Fluke,生產(chǎn)電子測(cè)試設(shè)備;我在里面做嵌入式系統(tǒng)編程)。有一位軟件工程師輔導(dǎo)我,教我使用emacs。不過(guò)當(dāng)時(shí)公司里唯一的工具只有Gosling Emacs的商用版(Unipress)。如果你做錯(cuò)了什么,程序會(huì)侮辱你,把你叫做火雞,并把屏幕填滿垃圾。這樣的東西出現(xiàn)在了一個(gè)商用產(chǎn)品上,而我們公司可是花了相當(dāng)一筆錢的。不消說(shuō),等到Gnu emacs變得穩(wěn)定起來(lái)后,公司馬上就換到了Gnu emacs上(我見過(guò)Richard Stallman。當(dāng)然,他是個(gè)瘋狂的家伙。不過(guò)他也是絕頂聰明的:他知道當(dāng)遇到麻煩的時(shí)候,你需要的是幫助,而不是侮辱)。(51CTO編者注:Richard Stallman即Gnu emacs的開發(fā)人員,美國(guó)一位著名黑客。他曾在05年坐客新浪,與洪峰大談黑客道培訓(xùn)。)
我不知道對(duì)Gosling印象的這段形成經(jīng)歷在多大程度上影響了我后面對(duì)他工作的看法,但事實(shí)上,“我們看見它太差勁了,就決定拿出自己的語(yǔ)言”,對(duì)C++的這種態(tài)度于事無(wú)補(bǔ)。尤其是當(dāng)我開始在《Java編程思想》的寫作過(guò)程中把它弄清楚,并屢次發(fā)現(xiàn),那些草率決定的語(yǔ)言特性與庫(kù),都不得不予以修訂——確實(shí)如此,其中的大部分都必須要修訂,有些修訂還是在程序員已經(jīng)忍受了多年之后才落實(shí)。在許多場(chǎng)合下,Gosling坦誠(chéng)他們必須快馬加鞭,否則就要被互聯(lián)網(wǎng)革命超越了。
我發(fā)現(xiàn),理解語(yǔ)言特性為什么會(huì)存在是非常有幫助的。如果是由大學(xué)教授一下子和盤托出,把它們端到你面前,你勢(shì)必就會(huì)構(gòu)想出這門語(yǔ)言的一個(gè)神話,說(shuō)“這種語(yǔ)言特性之所以存在,肯定有一些真正重要的原因,這些原因只有創(chuàng)建這門語(yǔ)言的聰明人才能理解,我是理解不了的,我信賴它就是了”。從某方面來(lái)說(shuō),對(duì)語(yǔ)言特性這種基于信仰的接受是一種負(fù)擔(dān);它阻止你對(duì)所發(fā)生的事情進(jìn)行分析和理解。在我的主旨演講中(Bruce將在未來(lái)幾天參與一個(gè)主旨演講),我會(huì)關(guān)注一些特性,并檢查一下它們?cè)诓煌Z(yǔ)言中是如何被實(shí)現(xiàn)的,以及為什么被實(shí)現(xiàn)。
這里就有個(gè)例子。對(duì)象創(chuàng)建。在C語(yǔ)言中,聲明了變量之后編譯器就會(huì)為你創(chuàng)建堆??臻g(未經(jīng)初始化,除非你初始化,否則會(huì)有垃圾數(shù)據(jù))。但是如果你想要?jiǎng)討B(tài)地做這件事情,你就得使用 malloc() 和 free()這兩個(gè)標(biāo)準(zhǔn)庫(kù)函數(shù),還要小心翼翼地手工執(zhí)行完所有的初始化及清理工作。如果你忘了,就會(huì)出現(xiàn)內(nèi)存泄漏等類似災(zāi)難,這是常有的事。
有關(guān)動(dòng)態(tài)對(duì)象創(chuàng)建:一般來(lái)說(shuō),編譯器將內(nèi)存分為三部分:靜態(tài)存儲(chǔ)區(qū)域、棧、堆。靜態(tài)存儲(chǔ)區(qū)主要保存全局變量和靜態(tài)變量,棧存儲(chǔ)調(diào)用函數(shù)相關(guān)的變量、地址等,堆存儲(chǔ)動(dòng)態(tài)生成的變量,在c中是指由malloc,free運(yùn)算產(chǎn)生釋放的存儲(chǔ)空間,在c++中就是指new和delete運(yùn)算符作用的存儲(chǔ)區(qū)域。(來(lái)源:51CTO樹洞的技術(shù)博客)
因?yàn)閙alloc() 和 free() “僅僅”是庫(kù)函數(shù),在基本編程課上,應(yīng)有的相關(guān)知識(shí)通常沒(méi)有被傳授,令人既疑惑不解又膽顫心驚。當(dāng)程序員需要分配大量的內(nèi)存空間時(shí),他們就不去學(xué)如何來(lái)使用這些函數(shù)進(jìn)行處理,取而代之的是常常就分配一個(gè)巨型數(shù)組的全局變量了事(不是開玩笑),數(shù)組之大,遠(yuǎn)遠(yuǎn)超過(guò)他們?cè)哉J(rèn)為所需的空間。程序似乎工作了,再說(shuō)了,好像誰(shuí)都不會(huì)用到產(chǎn)生越界——因此,當(dāng)多年之后它的確發(fā)生的時(shí)候,程序中斷了,而某個(gè)可憐的家伙就得一頭鉆進(jìn)去,把錯(cuò)誤在哪里這個(gè)謎底給找出來(lái)。
Stroustrup認(rèn)為動(dòng)態(tài)分配需要更簡(jiǎn)單、更安全——這一塊得放到語(yǔ)言核心中,而不是降格為庫(kù)函數(shù)。還必須要與初始化和清理一起協(xié)同工作,初始化和清理必須由構(gòu)造函數(shù)和析構(gòu)函數(shù)分別提供,以便為所有對(duì)象提供相同的保證。
這個(gè)問(wèn)題是影響了全部C++決策的一塊里程碑:對(duì)C的向后兼容性。理想情況下,對(duì)象的堆棧(heap)分配可只需忽略即可。但C的兼容性要求進(jìn)行堆棧(stack)分配,因此必須對(duì)heap對(duì)象和stack對(duì)象進(jìn)行區(qū)分。為了解決這個(gè)問(wèn)題,C++從SmallTalk挪用了new 這個(gè)關(guān)鍵字。創(chuàng)建 stack 對(duì)象只需聲明即可,像這樣: Cat x;或者帶參數(shù)的情況下, Cat x("mittens");。而創(chuàng)建heap 對(duì)象時(shí),就使用new,像這樣: new Cat x; 或者 new Cat x("mittens");。利用這個(gè)約束,我們得到一個(gè)優(yōu)雅而一致的解決方案。
自從判定C++的一切都做得不好且過(guò)于復(fù)雜之后,Java就產(chǎn)生了。具有諷刺意味的是,Java 決定把 stack 徹底拋棄了(特別是忽略了基本類型上的失敗,這點(diǎn)我已經(jīng)在別的地方指出過(guò)了)。真好啊,既然所有對(duì)象都是在heap上分配的,區(qū)分stack和heap的分配就沒(méi)有必要了。他們可以輕易說(shuō) Cat x = Cat() 或者 Cat x = Cat("mittens")。或者甚至更好地,用聯(lián)合的類型引用來(lái)消除重復(fù)(不過(guò)那樣——還有像閉包(closure)之類的其他特性——就顯得“太長(zhǎng)”了。因此我們反而離不開Java的平凡版;類型推導(dǎo)已經(jīng)討論過(guò)了,但我敢打賭那不會(huì)發(fā)生,也不該發(fā)生。因?yàn)檫@會(huì)在給Java增加特性的同時(shí)帶來(lái)問(wèn)題)。
Guido Van Rossum (Python的創(chuàng)建者)采用了一個(gè)最小化的方案——經(jīng)常為人所痛斥的空白的使用,正說(shuō)明了他對(duì)語(yǔ)言簡(jiǎn)潔性的追求。既然 new 關(guān)鍵字不再必要,他就省去了,好像這樣: x = Cat("mittens")。Ruby 也可能用了這種方法,不過(guò)Ruby其中一個(gè)主要的約束是盡可能追隨Smalltalk,因此在Ruby是這樣的:x = Cat.new("mittens") 。但Java以貶低C++做事的方式為準(zhǔn)則,以至于用了new 這個(gè)關(guān)鍵字成了一個(gè)迷了。自研究了該語(yǔ)言在其他地方所做的決策后,我的猜測(cè)是,他們是不是從來(lái)就沒(méi)有意識(shí)到,這東西根本就是可有可無(wú)的?
因此這就是我所說(shuō)的語(yǔ)言考古學(xué)的意思。我希望人們能用一個(gè)更好的視角來(lái)看待語(yǔ)言設(shè)計(jì),并在學(xué)習(xí)一門編程語(yǔ)言時(shí),能有更多的批判性思維過(guò)程。
本文選譯自Bruce Eckel在Artima的博文:Why? Language Archaeology ... and Metaprogramming
附錄:《Java編程思想》第四版章節(jié)預(yù)覽——51CTO讀書頻道
【編輯推薦】