轉(zhuǎn)載自:cool shell,作者:陳皓
From:http://coolshell.cn/articles/7992.html
先說明一下,我不希望本文變成語言爭論貼。希望下面的文章能讓我們客觀理性地了解C++這個語言。(另,我覺得技術(shù)爭論不要停留在非黑即白的二元價值觀上,這樣爭論無非就是比誰的嗓門大,比哪一方的觀點強,毫無價值。我們應(yīng)該多看看技術(shù)是怎么演進的,怎么取舍的。)
事由
周五的時候,我在我的微博上發(fā)了一個貼說了一下一個網(wǎng)友給我發(fā)來的C++程序的規(guī)范和內(nèi)存管理寫的不是很好(后來我刪除了,因為當(dāng)事人要求),我并非批判,只是想說明其實程序員是需要一些“疫苗”的,并以此想開一個“程序員疫苗的網(wǎng)站”,結(jié)果,@簡悅云風(fēng)同學(xué)直接回復(fù)到:“不要用 C++ 直接用 C , 就沒那么多坑了。”就把這個事帶入了語言之爭。
我又發(fā)了一條微博:
@左耳朵耗子 : 說C++比C的坑更多的人我可以理解,但理性地思考一下。C語言的坑也不少啊,如果說C語言有90個坑,那么C++就是100個坑(另,我看很多人都把C語言上的坑也歸到了C++上來),但是C++你得到的東西更多,封裝,多態(tài),繼承擴展,泛型編程,智能指針,……,你得到了500%東西,但卻只多了10%的坑,多值啊。
結(jié)果引來了更多的回復(fù)(只節(jié)選了一些言論):
- @淘寶褚霸也在微博里說:“自從5年前果斷扔掉C++,改用了ansi c后,我的生活質(zhì)量大大提升,沒有各種坑坑我。”
- @Laruence在其微博里說: “我確實用不到, C語言靈活運用struct, 可以很好的滿足這些需求.//@左耳朵耗子: 封裝,繼承,多態(tài),模板,智能指針,這也用不到?這也學(xué)院派?//@Laruence: 問題是, 這些東西我都用不到… C語言是工程師搞的, C++是學(xué)院派搞的”
那么,C++的坑真的多么?我還請大家理性地思考一下。
C++真的比C差嗎?
我們先來看一個圖——《各種程序員的嘴臟的對比》,從這個圖上看,C程序員比C++的程序員在注釋中使用fuck的字眼多一倍。這說明了什么?我個人覺得這說明C程序員沒有C++程序員淡定。
不要太糾結(jié)上圖,只是輕松一下,我沒那么無聊,讓我們來看點真正的論據(jù)。
相信用過C++的程序員知道,C++的很多特性主要就是解決C語言中的各種不完美和缺陷:(注:C89、C99中許多的改進正是從C++中所引進的)
- 用namespace解決了很C函數(shù)重名的問題。
- 用const/inline/template代替了宏,解決了C語言中宏的各種坑。
- 用const的類型解決了很多C語言中變量值莫名改變的問題。
- 用引用代替指針,解決了C語言中指針的各種坑。這個在Java里得到徹底地體現(xiàn)。
- 用強類型檢查和四種轉(zhuǎn)型,解決了C語言中亂轉(zhuǎn)型的各種坑。
- 用封裝(構(gòu)造,析構(gòu),拷貝構(gòu)造,賦值重載)解決了C語言中各種復(fù)制一個結(jié)構(gòu)體(struct)或是一個數(shù)據(jù)結(jié)構(gòu)(link, hashtable, list, array等)中淺拷貝的內(nèi)存問題的各種坑。
- 用封裝讓你可以在成員變量加入getter/setter,而不會像C一樣只有文件級的封裝。
- 用函數(shù)重載、函數(shù)默認(rèn)參數(shù),解決了C中擴展一個函數(shù)搞出來像func2()之類的ugly的東西。
- 用繼承多態(tài)和RTTI解決了C中亂轉(zhuǎn)struct指針和使用函數(shù)指針的諸多讓代碼ugly的問題。
- 用RAII,智能指針的方式,解決了C語言中因為出現(xiàn)需要釋放資源的那些非常ugly的代碼的問題。
- 用OO和GP解決各種C語言中用函數(shù)指針,對指針亂轉(zhuǎn)型,及一大砣if-else搞出來的ugly的泛型。
- 用STL解決了C語言中算法和數(shù)據(jù)結(jié)構(gòu)的N多種坑。
上述的這些東西填了不知有多少的C語言編程和維護的坑。少用指針,多用引用,試試autoptr,用用封裝,繼承,多態(tài)和函數(shù)重載…… 你面對的坑只會比C少,不會多。
C++的坑有多少?
C++的坑真的不多,如果你能花兩到三周的時候讀一下《Effecitve C++》里的那50多個條款,你就知道C++里的坑并不多,而且,有很多條款告訴我們C++是怎么解決C的坑的。然后,你可以讀讀《Exceptional C++》和《More Exceptional C++》,你可以了解一下C++各種問題的解決方法和一些常見的經(jīng)典錯誤。
當(dāng)然,C++在解決了很多C語的坑的同時,也因為OO和泛型又引入了一些坑。消一些,加一些,我個人感覺上總體上只比C多10%左右吧。但是你有了開發(fā)速度更快,代碼更易讀,更易維護的500%的利益。
另外,不可否認(rèn)的是,C++中的代碼出了錯誤,有時候很難搞,而且似乎用C++的人會覺得C++更容易出錯?我覺得主要是下面幾個原因:
- C和C++都沒學(xué)好,大多數(shù)人用C++寫C,所以,C的坑和C++的坑合并了。
- C++太靈活了,想怎么搞就怎么搞,所以,各種不經(jīng)意地濫用和亂搞。
另外,C++的編譯對標(biāo)準(zhǔn)C++的實現(xiàn)各異,支持地也千差萬別,所以會有一些比較奇怪的問題,但是如果你一般用用C++的封裝,繼承,多態(tài),以及namespace,const, refernece, inline, templete, overloap, autoptr,還有一些OO 模式,并不會出現(xiàn)奇怪的問題。
而對于STL中的各種坑,我覺得是程序員們還對GP(泛型編程)理解得還不夠,STL是泛型編程的頂級實踐!屬于是大師級的作品,一般人很難理解。必需承認(rèn)STL寫出來的代碼和編譯錯誤的確相當(dāng)復(fù)雜晦澀,太難懂了。這也是C++的一個詬病。
這和Linus說的一樣 —— “C++是一門很恐怖的語言,而比它更恐怖的是很多不合格的程序員在使用著它”。注意我飄紅了“很多不合格的程序員”!
我覺得C++并不適合初級程序員使用,C++只適合高級程序員使用(參看《21天學(xué)好C++》和《C++學(xué)習(xí)自信心曲線》),正如《Why C++》中說的,C++適合那些對開發(fā)維護效率和系統(tǒng)性能同時關(guān)注的高級程序員使用。
這就好像飛機一樣,開飛機很難,開飛機要注意的東西太多太多,對駕駛員的要求很高,但你不能說飛機這個工具很爛,開飛機的坑太多。(注:我這里并不是說C++是飛機,C是汽車,C++和C的差距,比飛機到汽車的差距少太多太多,這里主要是類比,我們對待C++語言的心態(tài)!)
C++的初衷
理解C++設(shè)計的最佳讀本是《C++演化和設(shè)計》,在這本書中Stroustrup說了些事:
1)Stroustrup對C是非常欣賞,實際上早期C++許多的工作是對于C的強化和凈化,并把完全兼容C作為強制性要求。C89、C99中許多的改進正是從C++中所引進。可見,Stroustrup對C語言的貢獻非常之大。今天不管你對C++怎么看,C++的確擴展和進化了C,對C造成了深遠(yuǎn)的影響。
2)Stroustrup對于C的抱怨主要來源于兩個方面——在C++兼容C的過程中遇到了不少設(shè)計實現(xiàn)上的麻煩;以及守舊的K&R C程序員對Stroustrup的批評。很多人說C++的惡夢就是要去兼容于C,這并不無道理(Java就干的比C++徹底得多),但這并不是Stroustrup考慮的,Stroustrup一邊在使盡渾身解數(shù)來兼容C,另一方面在拼命地優(yōu)化C。
3)Stroustrup在書中直接說,C++最大的競爭對手正是C,他的目的就是——C能做到的,C++也必須做到,而且要做的更好。大家覺得是不是做到了?有多少做到了,有多少還沒有做到?
4)對于同時關(guān)注的運行效率和開發(fā)效率的程序員,Stroustrup多次強調(diào)C++的目標(biāo)是——“在保證效率與C語言相當(dāng)?shù)那闆r下,加強程序的組織性;能保證同樣功能的程序,C++更短小”,這正是淺封裝的核心思想。而不是過渡設(shè)計的OO。(參看:面向?qū)ο笫莻€騙局)
5)這本書中舉了很多例子來回應(yīng)那些批評C++有運行性能問題的人。C++在其第二個版本中,引入了虛函數(shù)機制,這是C++效率最大的瓶頸了,但我個人認(rèn)為虛函數(shù)就是多了一次加法運算,但讓我們的代碼能有更好的組織,極大增加了程序的閱讀和降底了維護成本。(注:Lippman的《深入探索C++對象模型》也說明了C++不比C的程序在運行性能低。Bruce的《Think in C++》也說C++和C的性能相差只有5%)
6)這本書中還講了一些C++的痛苦的取舍,印象最深的就是多重繼承,提出,拿掉,再被提出,反復(fù)很多次,大家在得與失中不斷地辯論和取舍。這個過程讓我最大的收獲是——a) 對于任何一種設(shè)計都有好有壞,都只能偏重一方,b) 完全否定式的批評是不好的心態(tài),好的心態(tài)應(yīng)該是建設(shè)性地批評。
我對C++的感情
我先說說我學(xué)C++的經(jīng)歷。
我畢業(yè)時,是直接從C跳過C++學(xué)Java的,但是學(xué)Java的時候,不知道為什么Java要設(shè)計成這樣,只好回頭看C++,結(jié)果學(xué)C++的時候又有很多不懂,又只得回頭看C,最后發(fā)現(xiàn),C -> C++ -> Java的過程,就是C++填C的坑,Java填C++的坑的過程。
注,下面這些東西可以看到Java在填C/C++坑:
- Java徹底廢棄了指針(指針這個東西,絕對讓這個社會有幾百億的損失),使用引用。
- Java用GC解決了C++的各種內(nèi)存問題的詬病,當(dāng)然也帶來了GC的問題,不過功大于過。
- Java對異常的支持比C++更嚴(yán)格,讓編程更方便了。
- Java沒有像C++那樣的template/macro/函數(shù)對象/操作符重載,泛型太晦澀,用OO更容易一些。
- Java改進了C++的構(gòu)造、析構(gòu)、拷貝構(gòu)造、賦值。
- Java對完全拋棄了C/C++這種面向過程的編程方式,并廢棄了多重繼承,更OO(如:用接口來代替多重繼承)
- Java比較徹底地解決了C/C++自稱多年的跨平臺技術(shù)。
- Java的反射機制把這個語言提升了一個高度,在這個上面可以構(gòu)建各種高級用法。
- C/C++沒有一些比較好的類庫,比如UI,線程 ,I/O,字符串處理等。(C++0x補充了一些)
- 等等……
當(dāng)然時代還在前進,這個演變的過程還在C#和Go上體現(xiàn)著。不過我學(xué)習(xí)了C -> C++ -> Java這個填坑演進的過程,讓我明白了很多東西:
- 我明白了OO是怎么一回事,重要的是明白了OO的封裝,繼承,和多態(tài)是怎么實現(xiàn)的。(參看我以前寫過的《C++虛函數(shù)表解析》和《C++對象內(nèi)存布局》)
- 我明白了STL的泛型編程和Java的各種花哨的技術(shù)是怎么一回事,以及那些很花哨的編程方法和技術(shù)。
- 我明白了C,C++,Java的各中坑,這就好像玩火一樣,我知道怎么玩火不會燒身了。
我從這個學(xué)習(xí)過程中得到的最大的收獲不是語言本身,而是各式各樣的編程技術(shù)和方法,和技術(shù)的演進的過程,這比語言本身更重要!(在這個角度上學(xué)習(xí),你看到的不是一個又一個的坑,你看到的是——各式各樣讓你可以爬得更高的梯子)
我對C++的感情有三個過程:先是喜歡地要死,然后是恨地要死,現(xiàn)在的又愛又恨,愛的是這個語言,恨的是很多不合格的人在濫用和凌辱它。
C++的未來
C++語言發(fā)展大概可以分為三個階段(摘自Wikipedia):
- 第一階段從80年代到1995年。這一階段C++語言基本上是傳統(tǒng)類型上的面向?qū)ο笳Z言,并且憑借著接近C語言的效率,在工業(yè)界使用的開發(fā)語言中占據(jù)了相當(dāng)大份額;
- 第二階段從1995年到2000年,這一階段由于標(biāo)準(zhǔn)模板庫(STL)和后來的Boost等程式庫的出現(xiàn),泛型程式設(shè)計在C++中占據(jù)了越來越多的比重性。當(dāng)然,同時由于Java、C#等語言的出現(xiàn)和硬件價格的大規(guī)模下降,C++受到了一定的沖擊;
- 第三階段從2000年至今,由于以Loki、MPL等程式庫為代表的產(chǎn)生式編程和模板元編程的出現(xiàn),C++出現(xiàn)了發(fā)展歷史上又一個新的高峰,這些新技術(shù)的出現(xiàn)以及和原有技術(shù)的融合,使C++已經(jīng)成為當(dāng)今主流程式設(shè)計語言中最復(fù)雜的一員。
在《Why C++? 王者歸來》中說了 ,性能主要就是要省電,省電就是省錢,在數(shù)據(jù)中心還不明顯,在手機上就更明顯了,這就是為什么Android 支持C++的原因。所以,在NB的電池或是能源出現(xiàn)之前,如果你需要注重程序的運行性能和開發(fā)效率,并更關(guān)注程序的運性能,那么,應(yīng)該首選 C++。這就是iOS開發(fā)也支持C++的原因。
今天的C++11中不但有更多更不錯的東西,而且,還填了更多原來C++的坑。(參看:C++11 Wiki,C++ 11的主要特性)
總結(jié)
- C++并不完美,但學(xué)C++必然讓你受益無窮。
- 是那些不合格的、想對編程速成的程序員讓C++變得坑多。
最后,非常感謝能和“@簡悅云風(fēng)”,“@淘寶諸霸”,“@Laruence”一起討論這個問題!無論你們的觀點怎么樣,我都和你們“在一起”,嘿嘿嘿……
(全文完)