C++不垃圾,只是Java很傲慢

 本文轉自:http://developer.51cto.com/art/200906/131773.htmc++

    

    《Thinking in C++》及《Thinking in Java》的做者Bruce Eckel向來是個「擁C++反Java」派,他曾經不止一次的提到,C++語言特性的添加有多麼的深思熟慮,而Java又是如何的把一些奇怪的東西不停的加進去。Bruce認爲,理解語言特性爲何會存在是很是有幫助的。他將其稱之爲「語言考古學」。程序員

    在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 這個關鍵字成了一個迷了。自研究了該語言在其餘地方所作的決策後,個人猜想是,他們是否是歷來就沒有意識到,這東西根本就是無關緊要的?

    所以這就是我所說的語言考古學的意思。我但願人們能用一個更好的視角來看待語言設計,並在學習一門編程語言時,能有更多的批判性思惟過程。

相關文章
相關標籤/搜索