不得不看的兩次從C++回歸C的高手評論C++
Linux之父炮轟C++:糟糕程序員的垃圾語言 |
||
Linux之父話糙理不糙 | 不得不看的兩次從C++回歸C的高手評論C++ | C語言是否該扔進垃圾桶 |
為什么每個程序員都應該學習C語言? | 每個程序員都應該學習C語言?我可不這么認為 | C語言已經死了,5個需要忘卻它的理由 |
C++0x:嶄新的C++,還是另一個Java? | ||
編程語言的三大定理 | 動態語言為何難堪重任 | 動態語言面面觀 |
【引自雲風的博客】首先,給不熟悉我的朋友做一個技術背景的自我介紹:
我不是一個Linux的fans,雖然我今天對Windows也沒有什么好感,但我的大部分工作還是在Windows上做應用軟件開發的,對Windows還算熟悉。現在我也用非Windows的系統,但那是一台FreeBSD的機器,不是Linux。
我自認為對C++相當熟悉,精讀過市面上能買到的關於C++的大部分書籍,像D&E of C++這樣的經典還讀了不只一遍。用C++寫過至少數十萬行代碼,閱讀過STL的大部分源碼,和ACE/Boost的一小部分。
曾經我是C++的忠實粉絲,如果誰說C++的不是,要么會選擇跟他辯論到底,要么會對此人不屑一顧。
還有一點我認為非常重要:我第一次愛上C++是15年前(1992年),然后對其慢慢冷淡,回歸C的懷抱。而到了2000年,我又一次愛上C++。也就是說,從熱愛C++到否定它,在我的個人經歷中,有過兩次。不排除未來有第三次的可能,但這一點足可說明,否定C++是出於一種理性的判斷,而不是一種沖動。
寫上這些,並非是想倚老賣老。我知道,想罵我的C++程序員,更討厭有人倚老賣老的數落C++的不是。而且論資格,我頂多及的上Linus之父的一個零頭,既然有老人在前撐腰,下面說話的底氣就可以足一些了:)
C是C++的一個子集(從C99開始已經不是了),用C能寫出來的代碼,C++一樣可以寫出來,然后可以完成的更好。
這是新手們自以為是的攻擊武器。Linus用了一個很恰當的理由做出反擊:“你當然可以用任何語言編寫糟糕的代碼。但是,有些語言,尤其是帶有一些心智(mental)包袱的語言本身就非常糟糕。”
沒錯,我最想說的就是這個。C++就是一個“帶有一些心智(mental)包袱的語言”。這對軟件設計的影響非常之大,沒有經年的軟件開發實踐很難理解這一點。
從這一點上展開,把ASM和C比較的問題和C與C++的比較相提並論就沒有意義了。
接下來要找到的問題要點就是,C++比C多出來那些東西后,真的會帶來心智包袱嗎?這個問題不好回答。單純從C++語言特性的繁雜導致的不易掌握和誤用這些角度是很難說服我自己的,更別說去說服那些比我聰明的多,刻苦的多的C++程序員們。我自認為對所謂C++的高級特性掌握的還是不錯的,並運用在諸多實際項目中。他們相當有趣,在某種程度上也非常的有效。代碼可以獲得相當高的執行效率,並可以縮短編碼的時間(更少的點擊數),完成他們也有很大的成就感。
好了,讓我再引用Linus的一句說到我心坎里的話。“字符串/內存管理根本無關緊要。這不是重要的部分,而且也不復雜。唯一真正重要的部分是設計。”
設計!這才是重中之重。
如果要說,這最近10年的程序員生涯我學會了什么?我認為,我比以前能設計出更好的代碼了。能更准確的把握設計的壞味道。而對編程語言的掌握,對操作系統的熟悉,工作相關知識的了解等等。那些只是自然而然發生的事,那些是知識的積累,而非能力的提高。
“抽象”,“面向對象”,“設計模式”,這些重要嗎?重要。對軟件開發相當重要。但重要不是必要,執迷於“抽象”會使你離目標越來越遠。當我們一次又一次的提取出事物的共性,建立起抽象層的時候,我們可能丟棄了真實。C++繼承了C語言中“信任程序員”這一設計哲學,致力於讓程序員在建立抽象層時,可以不做出額外的消耗。他的解決方式是提供盡可能多的語言工具和設計選擇,任何一個都允許你在不用的時候不帶來額外的性能損失。
這是一個美好的願景:C++程序員指望可以建立強大的可復用的抽象層,面對世界上一切的具體應用。同時CPU執行序列在穿越這個堅厚的抽象層的過程中,居然可以以光速通過(通過抽象層沒有額外的執行效率付出)。為此:C++社區創造了STL,創造了Boost。它們共同的關鍵詞是:效率、復用。
再往上呢?另一個問題產生了:“——低效的抽象編程模型,可能在兩年之后你會注意到有些抽象效果不怎么樣,但是所有代碼已經依賴於圍繞它設計的‘漂亮’對象模型了,如果不重寫應用程序,就無法改正。”這一段依舊是Linus語,我不停的引用,是因為我明白這一點,但是不能表達的更清楚。
使用C++的程序員不斷的強調復用性,卻不斷的需要重寫代碼。如果一段代碼可以不被重寫,那多半是因為對重寫工程量的妥協。是的,其實我們可以用C++的各種特性寫出更好,更漂亮,更高效的代碼。兩年前的框架不那么完美,不是C++語言的錯,是兩年前的我能力有限的緣故。但是因為需要改寫的是設計框架,這意味着我們必須跟着變更已經完成的功能模塊,或是加上橋接層。
的確,STL和Boost都是世界頂尖程序員完成的。代碼質量非常的高(當然,我對Boost的一部分持保留意見)。我不拿編譯器兼容性和可移植性或是編譯速度說事,雖然這些的確是現實問題,但不足以成為反對C++基礎類庫的理由。
好好的用好C++當然得用好STL,Boost也應該認真考察一下。能夠仔細讀一下源碼更好。合格的C++程序員應該做這個。否則作為C++程序員你就違背了C++語言的設計哲學:C++信任了你,你就該對的起這種信任,搞清楚你寫的每一行代碼背后,機器都去干了什么。
但是,STL過於龐大了,Boost更加是。我不是抱怨閱讀和學習它們的源碼的難度和需要的時間和精力。正相反,我在學習它們的過程中充滿了樂趣和感激之情。高手前輩透過這些高質量的代碼教會了我很多東西。我隱隱擔心的是,這么龐大的代碼,它的設計不可能是永遠正確的。兩年之后,他們的設計肯定依舊正確,再兩年還是的。但是我幾乎敢肯定,放之更長遠的時間來看,絕對會在某些設計領域發現其不是最佳的選擇。到那一天,我們會選擇修改嗎?我想C++社區會被迫選擇妥協。但是,C++程序員心中會充滿痛苦。
C在這個問題上的抉擇是不一樣的。在效率問題上,C程序里最令人擔心的是函數調用的消耗。C++程序員最津津樂道的案例就是std::sort全面擊敗了C庫中的qsort。C語言的失敗正在於多余的函數調用消耗。
但是,從一開始C就選擇了承認函數調用的消耗,而這一點幾乎是唯一。付出了這個代價后,設計失誤導致的效率下降問題幾乎總可以避免。C和C++都可以選擇重寫設計失敗的部分,但不一樣的是,C程序員幾乎可以不考慮妥協的問題。同樣的是考慮極端效率的語言,C語言坦然面對缺陷,才是真正的符合了KISS原則。
我對這個問題的見解,可以再引用Linus的一段話作為收場。“如果你想用更花哨的語言,C++絕對是最糟糕的選擇。如果想要真正的高級特性,那就選擇有垃圾回收或者好的系統集成的,而不是既缺乏C的簡約(sparseness)又缺乏C的直接而且沒有重要概念的高層綁定(high-level bindings to important concepts)的東西。”。這是我最近幾年來一直堅持的觀點:C++的發展,一定要補充對GC支持所需要的特性。
強調一下,我並不討厭C++。
ps.最近兩年多,我在做一個游戲引擎的項目。這個項目現在是第三個版本了。第一個版本是用C++實現的,但是沒有用任何已存在的類庫(包括STL)。在第二個版本中,我去掉了所有使用C++高級特性實現的部分,只使用了C++基本特性實現所有。07年重寫的第三個版本,全部換成C代碼了。這個項目的發展,可以反應出我個人對C/C++理解的心路過程。