【C++避坑相關(guān)】云風(fēng)的BLOG:C的回歸
來(lái)源鏈接:https://blog.codingnow.com/2007/09/c_vs_cplusplus.html
周末出差,去另一個(gè)城市給公司的一個(gè)項(xiàng)目解決點(diǎn)問(wèn)題。回程去機(jī)場(chǎng)的路上,我用手機(jī)上 google reader 打發(fā)時(shí)間。第一眼就看到孟巖大大新的一篇:Linux之父話糙理不糙 。主題是 C 與 C++ 的語(yǔ)言之爭(zhēng)。轉(zhuǎn)到劉江的 blog 下讀完了 Linux之父炮轟C++:糟糕程序員的垃圾語(yǔ)言 大呼過(guò)癮。立刻把鏈接短信發(fā)給了幾個(gè)朋友。
語(yǔ)言之爭(zhēng)永遠(yuǎn)是火藥味十足的話題。尤其是 C 和 C++ 的目標(biāo)市場(chǎng)又有很高的重合性,C++ 程序員往往對(duì)C++ 其有著宗教般的虔誠(chéng)。我想,今天我在自己的 blog 上繼續(xù)這個(gè)戰(zhàn)爭(zhēng),一定會(huì)換來(lái)更多的罵名。只不過(guò)這次 Linus 幾句話真是說(shuō)到我心坎里去了,不喊出來(lái)會(huì)憋壞的 :D
首先,給不熟悉我的朋友做一個(gè)技術(shù)背景的自我介紹:
我不是一個(gè) Linux 的 fans ,雖然我今天對(duì) Windows 也沒(méi)有什么好感,但我的大部分工作還是在 WIndows 上做應(yīng)用軟件開(kāi)發(fā)的,對(duì) Windows 還算熟悉。現(xiàn)在我也用非 Windows 的系統(tǒng),但那是一臺(tái) FreeBSD 的機(jī)器,不是 Linux 。
我自認(rèn)為對(duì) C++ 相當(dāng)熟悉,精讀過(guò)市面上能買到的關(guān)于 C++ 的大部分書籍,像 D&E of C++ 這樣的經(jīng)典還讀了不只一遍。用 C++ 寫過(guò)至少數(shù)十萬(wàn)行代碼,閱讀過(guò) STL 的大部分源碼,和 ACE / Boost 的一小部分。
曾經(jīng)我是 C++ 的忠實(shí)粉絲,如果誰(shuí)說(shuō) C++ 的不是,要么會(huì)選擇跟他辯論到底,要么會(huì)對(duì)此人不屑一顧。
還有一點(diǎn)我認(rèn)為非常重要:我第一次愛(ài)上 C++ 是 15 年前(1992 年),然后對(duì)其慢慢冷淡,回歸 C 的懷抱。而到了 2000 年,我又一次愛(ài)上 C++ 。也就是說(shuō),從熱愛(ài) C++ 到否定它,在我的個(gè)人經(jīng)歷中,有過(guò)兩次。不排除未來(lái)有第三次的可能,但這一點(diǎn)足可說(shuō)明,否定 C++ 是出于一種理性的判斷,而不是一種沖動(dòng)。
寫上這些,并非是想倚老賣老。我知道,想罵我的 C++ 程序員,更討厭有人倚老賣老的數(shù)落 C++ 的不是。而且論資格,我頂多及的上 Linus 大大的一個(gè)零頭,既然有老人在前撐腰,下面說(shuō)話的底氣就可以足一些了 :)
C 是 C++ 的一個(gè)子集(從 C99 開(kāi)始已經(jīng)不是了),用 C 能寫出來(lái)的代碼,C++ 一樣可以寫出來(lái),然后可以完成的更好。
這是新手們自以為是的攻擊武器。Linus 用了一個(gè)很恰當(dāng)?shù)睦碛勺龀龇磽簦骸澳惝?dāng)然可以用任何語(yǔ)言編寫糟糕的代碼。但是,有些語(yǔ)言,尤其是帶有一些心智(mental)包袱的語(yǔ)言本身就非常糟糕。”
沒(méi)錯(cuò),我最想說(shuō)的就是這個(gè)。C++ 就是一個(gè)“帶有一些心智(mental)包袱的語(yǔ)言”。這對(duì)軟件設(shè)計(jì)的影響非常之大,沒(méi)有經(jīng)年的軟件開(kāi)發(fā)實(shí)踐很難理解這一點(diǎn)。
從這一點(diǎn)上展開(kāi),把 ASM 和 C 比較的問(wèn)題和 C 與 C++ 的比較相提并論就沒(méi)有意義了。
接下來(lái)要找到的問(wèn)題要點(diǎn)就是,C++ 比 C 多出來(lái)那些東西后,真的會(huì)帶來(lái)心智包袱嗎?這個(gè)問(wèn)題不好回答。單純從 C++ 語(yǔ)言特性的繁雜導(dǎo)致的不易掌握和誤用這些角度是很難說(shuō)服我自己的,更別說(shuō)去說(shuō)服那些比我聰明的多,刻苦的多的 C++ 程序員們。我自認(rèn)為對(duì)所謂 C++ 的高級(jí)特性掌握的還是不錯(cuò)的,并運(yùn)用在諸多實(shí)際項(xiàng)目中。他們相當(dāng)有趣,在某種程度上也非常的有效。代碼可以獲得相當(dāng)高的執(zhí)行效率,并可以縮短編碼的時(shí)間(更少的鍵擊數(shù)),完成他們也有很大的成就感。
好了,讓我再引用 Linus 的一句說(shuō)到我心坎里的話?!白址?內(nèi)存管理根本無(wú)關(guān)緊要。這不是重要的部分,而且也不復(fù)雜。唯一真正重要的部分是設(shè)計(jì)?!?/p>
設(shè)計(jì)!這才是重中之重。
如果要說(shuō),這最近 10 年的程序員生涯我學(xué)會(huì)了什么?我認(rèn)為,我比以前能設(shè)計(jì)出更好的代碼了。能更準(zhǔn)確的把握設(shè)計(jì)的壞味道。而對(duì)編程語(yǔ)言的掌握,對(duì)操作系統(tǒng)的熟悉,工作相關(guān)知識(shí)的了解等等。那些只是自然而然發(fā)生的事,那些是知識(shí)的積累,而非能力的提高。
“抽象”,“面向?qū)ο蟆?,“設(shè)計(jì)模式”,這些重要嗎?重要。對(duì)軟件開(kāi)發(fā)相當(dāng)重要。但重要不是必要,執(zhí)迷于“抽象”會(huì)使你離目標(biāo)越來(lái)越遠(yuǎn)。當(dāng)我們一次又一次的提取出事物的共性,建立起抽象層的時(shí)候,我們可能丟棄了真實(shí)。C++ 繼承了 C 語(yǔ)言中“信任程序員”這一設(shè)計(jì)哲學(xué),致力于讓程序員在建立抽象層時(shí),可以不做出額外的消耗。他的解決方式是提供盡可能多的語(yǔ)言工具和設(shè)計(jì)選擇,任何一個(gè)都允許你在不用的時(shí)候不帶來(lái)額外的性能損失。
這是一個(gè)美好的愿景:C++ 程序員指望可以建立強(qiáng)大的可復(fù)用的抽象層,面對(duì)世界上一切的具體應(yīng)用。同時(shí) CPU 執(zhí)行序列在穿越這個(gè)堅(jiān)厚的抽象層的過(guò)程中,居然可以以光速通過(guò)(通過(guò)抽象層沒(méi)有額外的執(zhí)行效率付出)。為此:C++ 社區(qū)創(chuàng)造了 STL ,創(chuàng)造了 Boost 。它們共同的關(guān)鍵詞是:效率、復(fù)用。
再往上呢?另一個(gè)問(wèn)題產(chǎn)生了:“——低效的抽象編程模型,可能在兩年之后你會(huì)注意到有些抽象效果不怎么樣,但是所有代碼已經(jīng)依賴于圍繞它設(shè)計(jì)的‘漂亮’對(duì)象模型了,如果不重寫應(yīng)用程序,就無(wú)法改正?!边@一段依舊是 Linus 語(yǔ),我不停的引用,是因?yàn)槲颐靼走@一點(diǎn),但是不能表達(dá)的更清楚。
使用 C++ 的程序員不斷的強(qiáng)調(diào)復(fù)用性,卻不斷的需要重寫代碼。如果一段代碼可以不被重寫,那多半是因?yàn)閷?duì)重寫工程量的妥協(xié)。是的,其實(shí)我們可以用 C++ 的各種特性寫出更好,更漂亮,更高效的代碼。兩年前的框架不那么完美,不是 C++ 語(yǔ)言的錯(cuò),是兩年前的我能力有限的緣故。但是因?yàn)樾枰膶懙氖窃O(shè)計(jì)框架,這意味著我們必須跟著變更已經(jīng)完成的功能模塊,或是加上橋接層。
的確,STL 和 Boost 都是世界頂尖程序員完成的。代碼質(zhì)量非常的高(當(dāng)然,我對(duì) Boost 的一部分持保留意見(jiàn))。我不拿編譯器兼容性和可移植性或是編譯速度說(shuō)事,雖然這些的確是現(xiàn)實(shí)問(wèn)題,但不足以成為反對(duì) C++ 基礎(chǔ)類庫(kù)的理由。
好好的用好 C++ 當(dāng)然得用好 STL ,Boost 也應(yīng)該認(rèn)真考察一下。能夠仔細(xì)讀一下源碼更好。合格的 C++ 程序員應(yīng)該做這個(gè)。否則作為 C++ 程序員你就違背了 C++ 語(yǔ)言的設(shè)計(jì)哲學(xué):C++ 信任了你,你就該對(duì)的起這種信任,搞清楚你寫的每一行代碼背后,機(jī)器都去干了什么。
但是,STL 過(guò)于龐大了,Boost 更加是。我不是抱怨閱讀和學(xué)習(xí)它們的源碼的難度和需要的時(shí)間和精力。正相反,我在學(xué)習(xí)它們的過(guò)程中充滿了樂(lè)趣和感激之情。高手前輩透過(guò)這些高質(zhì)量的代碼教會(huì)了我很多東西。我隱隱擔(dān)心的是,這么龐大的代碼,它的設(shè)計(jì)不可能是永遠(yuǎn)正確的。兩年之后,他們的設(shè)計(jì)肯定依舊正確,再兩年還是的。但是我?guī)缀醺铱隙?,放之更長(zhǎng)遠(yuǎn)的時(shí)間來(lái)看,絕對(duì)會(huì)在某些設(shè)計(jì)領(lǐng)域發(fā)現(xiàn)其不是最佳的選擇。到那一天,我們會(huì)選擇修改嗎?我想 C++ 社區(qū)會(huì)被迫選擇妥協(xié)。但是,C++ 程序員心中會(huì)充滿痛苦。
C 在這個(gè)問(wèn)題上的抉擇是不一樣的。在效率問(wèn)題上,C 程序里最令人擔(dān)心的是函數(shù)調(diào)用的消耗。C++ 程序員最津津樂(lè)道的案例就是 std::sort 全面擊敗了 C 庫(kù)中的 qsort 。C 語(yǔ)言的失敗正在于多余的函數(shù)調(diào)用消耗。
但是,從一開(kāi)始 C 就選擇了承認(rèn)函數(shù)調(diào)用的消耗,而這一點(diǎn)幾乎是唯一。付出了這個(gè)代價(jià)后,設(shè)計(jì)失誤導(dǎo)致的效率下降問(wèn)題幾乎總可以避免。C 和 C++ 都可以選擇重寫設(shè)計(jì)失敗的部分,但不一樣的是, C 程序員幾乎可以不考慮妥協(xié)的問(wèn)題。同樣的是考慮極端效率的語(yǔ)言,C 語(yǔ)言坦然面對(duì)缺陷,才是真正的符合了 KISS 原則。
我對(duì)這個(gè)問(wèn)題的見(jiàn)解,可以再引用 Linus 的一段話作為收?qǐng)?。“如果你想用更花哨的語(yǔ)言,C++絕對(duì)是最糟糕的選擇。如果想要真正的高級(jí)特性,那就選擇有垃圾回收或者好的系統(tǒng)集成的,而不是既缺乏C的簡(jiǎn)約(sparseness)又缺乏C的直接而且沒(méi)有重要概念的高層綁定(high-level bindings to important concepts)的東西?!薄_@是我最近幾年來(lái)一直堅(jiān)持的觀點(diǎn):C++ 的發(fā)展,一定要補(bǔ)充對(duì) GC 支持所需要的特性。
強(qiáng)調(diào)一下,我并不討厭 C++ :) 。 C++ 的粉絲們可以隨便罵我,但是不要帶上階級(jí)仇恨。
ps. 最近兩年多,我在做一個(gè)游戲引擎的項(xiàng)目。這個(gè)項(xiàng)目現(xiàn)在是第三個(gè)版本了。第一個(gè)版本是用 C++ 實(shí)現(xiàn)的,但是沒(méi)有用任何已存在的類庫(kù)(包括 STL)。在第二個(gè)版本中,我去掉了所有使用 C++ 高級(jí)特性實(shí)現(xiàn)的部分,只使用了 C++ 基本特性實(shí)現(xiàn)所有。今年重寫的第三個(gè)版本,全部換成 C 代碼了。這個(gè)項(xiàng)目的發(fā)展,可以反應(yīng)出我個(gè)人對(duì) C/C++ 理解的心路過(guò)程。