[轉]關於C++複雜性的零碎思考

 

C++的表面困境來自兩方面,一是開發效率低,而是容易犯錯,維護難度大。此二者俱是表象,本質就是一個——過度複雜。或有人說C++之關鍵缺陷是沒有統一完整的類庫支撐,Bjarne Stroustrup即強調此因素。然而這其實只不過是一個結果,而不是原因。正是因爲語言太複雜,才無法在有效期內開發出高質量的大一統的類庫。

C++的複雜,並非是其體積龐大之必然結果。複雜是對結構混亂無序程度的描述,規模大,結構不見得必然複雜。

C++的複雜,也並不是如很多人所認爲,是若干種編程範式(paradigms)的並存而至。事實上,現代實用編程語言至少有2-3種範式才能登大雅之堂。以範式數量論,Python和Ruby等新型動態語言的範式甚至多於C++,然而它們卻以簡單和開發效率高著稱。

C++複雜的根源在於三大約束:與C的完全兼容、靜態類型檢查、最高性能。在三大約束下,C++未能完善對於面向對象思想的支持,未能建立強大的動態能力,從而使得C++在OO這個單項上存在本質缺陷。事實上,C++的過程、OB模型相當成熟和穩定,而泛型模型,就單項來說,除了語法醜陋之外也沒有大的問題。缺陷集中體現在OO模型的實現,並因此干擾了其他幾個範式的完整程度。然而,OO的缺陷絕非設計者的偏執,其原因在於三大約束。如果堅持三大約束,則即使再重新設計一次,結果也與今日相差不遠。Stroustrup在多種場合表示,對C++的設計沒有大的後悔之處,意思就是這個。侯捷先生早在2001年初即對我說,C++在OO上不及Java,當時體會不深,認爲沒有大一統的單根類庫會使設計更加靈活,後來又認爲憑藉GP可以抵消OO的不足甚至超越之,現在看來即使不是不可能,這條道路也必然是艱辛異常,成敗難以預料。

又因爲上述所有因素的綜合作用,C++基礎類庫的建設只能進行到很低的高度上就停下來,因爲再往上走就面臨重重困境和無窮無盡的爭論。C++標準庫實際上是一個距離應用相當遙遠的非常基礎的程序庫,其主體部分只相當於Java中System和Util兩個package。而C++寧可停在這樣的低層次,也不願意放棄三大約束中的任何一個。這種執着使得高層標準庫設施的建立異常困難,使用也不容易。Boost庫中相當部分組件的易用性不佳。

模板的複雜語法與三大約束也有直接的關係。另一個原因是Bjarne在發明模板時目標單純。C#和Java加入泛型機制的時候,沒有繼承C++最好的經驗,卻不約而同地繼承了C++模板機制中最壞的部分——語法,短期來看,喪失了一次改革的良機。長遠來看,必成累贅。

不完善的異常機制則是在木已成舟的情況下迫不得已的設計。

C++中的多種範式並行,是一些最複雜問題的表面原因。以至於Doug Lea建議在一個項目裏只堅持一個範式。但是這仍然只是表象。歸根結底還是因爲OO的缺陷,使得與其它範式合作時困難成倍放大。故自接受Doug Lea思想以來,我的C++(乃至其他現代語言,尤其是Python等多範式語言)的開發設計思路是:

1. 首先選定一種思維方式(即範式),儘可能只用這一種思維方式解決問題;
2. 如果在局部遇到其他思維方式更得力的問題,則經慎重考慮後,可以將另一種風格包裝在局部,解決局部問題。但整個系統在某一層次之上看來,應當是統一一致的。一般C++的開發,應以OB爲基本風格。除非有類似MFC那樣龐大而成熟的OO庫支持,不應貿然在整體上使用OO風格。
3. 多種風格混用,除非有已被充分討論並驗證的方案(即成熟模式),可提供單一風格不能提供的較大優勢,否則應極力避免。當然鼓勵在研究中探索,但實踐是另一回事。

C++完全可以在90年左右擺脫C的約束,隨後簡化模板語法,完善異常模型,接納可選GC,建立完整的單根類庫,付出性能小幅度下降的代價之後,實現語言整體升級。

但是C++選擇了另一條路,三大約束堅持到底,堅守系統層面,以替代C爲己任。是福是禍,實難判別。如果90年代初選擇升級,勝則扼死Java於搖籃之中,敗則寸土不保。不過以C++之高性能,勝面應稍大。如今看來,在系統面徹底取代C已無可能。

1994年爲STL拖延標準立案時間長達四年,如今來看功過亦存爭議。錯過黃金時機不說,STL典範一立,庫設計風氣爲之一改。然而在解決應用問題上,泛型較之OO,適應能力遠遜之,且應用困難。

總之,C++的三大約束,既是其興起之要素,也是其衰落之源頭,同時,又是其今天得以屹立不倒的重要基石。其是非功過,實難一言以蔽之。

補充:2004年10月20日

C++/CLI之對於C++的意義,其實並不在於使C++重新獲得了制勝Java或者C#的機會,而在於鞏固了C++作爲.NET平臺上系統語言的地位。由此知,C++/CLI的發展,的確如Stan Lippman所說,是C++一貫發展思路的延續。三大約束固然已經放棄,但其精神實質仍在,形攻而實守,未來將可作爲.NET上唯一最強之系統語言而長命百歲。

C++/CLI決不簡單,但在大多數時候,它能夠比傳統的C++表現的簡單些。這就是Andrew Koenig說的,通過複雜實現簡單。

C#和Java的繁榮期,則有賴於人們對於大一統的中層次語言的信仰有多堅持。此兩種語言無論在系統開發還是在應用開發中都非最優選。目前C#出現一些跡象,引入一些動態語言特性如cmdlet,又強化系統編程能力,想上下通吃。這是一條不歸路,必會使C#變得更加複雜怪異。

學習編程語言,通語法能實踐,不過十分之一。真正重要的是掌握其多種多樣的實用的idioms或模式。這些模式纔是體現了語言精神的東西。未掌握各種語言中的主要應用模式,則應羞於用“會”字。常聽有人說某某語言一週乃至一兩天即可掌握,這個掌握的層次肯定是很低的。真正要“掌握”語言,則我等凡人,諸事纏身,非得集中精力學習實踐一兩年,將該語言所擅長領域的應用問題熟悉過一遍,纔有可能。若論精通,則十年也不容易。Henry Spencer用了30年C,仍樂此不疲;Pragmatic Programmer中評價Ruby說,學上四個小時就可以用它解決實際問題,但是10年之後還爲它層出不窮的新意感到驚訝。偶見有人舉出自己“精通和掌握”的工具和語言,動輒長達八九上十種,實爲笑柄。真正掌握一種,已經是難能可貴,熟練掌握兩種層次不同,思維不同的語言,應是有抱負的程序員的自我要求。何況如今之軟件開發涉獵甚廣,僅通編程層次還顯不夠。不過總之百招會不如一招精,做什麼工作都要有自己的過人之處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章