1. 程式人生 > >【推薦看】什麼時候用C而不用C++?

【推薦看】什麼時候用C而不用C++?

前兩天不是有一個問題是“什麼時候用C++而不用C”,我一直覺得問錯了,難道不是“能用C++就不用C”麼?那麼當然就要討論什麼時候用C而不用C++啦。

一直以來都嚴格遵循OO的原則來進行開發(用的工具是C#和Qt),直到最近,開始接手某同事的程式碼,整個專案20多個小工程(程式碼量並不多),除了介面部分用了MFC這種不倫不類的OO以外,所有的程式碼都是C寫的。但是模組化做的非常好。後來跟他討論為何不用C++,他說其實沒有什麼特別的,就是習慣和愛好而已,後又補充:

如果不用多型的話,其實不管怎麼寫,不管用那種語言寫,都算不上真正的OO

忽然覺得很有道理……

其實這是一個好問題。

題主開始欣賞到純 C程式碼所帶來的 “美感” 了

,即簡單性可拆分性。程式碼是自底向上構造一個模組只做好一個模組的事情,任意拆分組合

對於有參考的 OOP系統建模,自頂向下的構造程式碼抽象方法有效率的,是方便的對於新領域,沒有任何參考時,刻意抽象會帶來額外負擔,並進一步增加系統耦合性,設計調整,往往需要大面積修改程式碼。

有興趣你可以讀讀《Unix程式設計藝術》,OOP的思維模式,是大一統的;C的思維模式,是分離的

前者方便但容易造成高耦合,後者靈活但開發開發太累。用 C開發,應該刻意強調 “簡單” 和 “可拆分”。一個個象搭積木一樣的把基礎系統搭建出來,哪個模組出問題,區域性替換即可。

自底向上的開發模式,並不是從不站在大局考慮問題,而是從某個子系統具體實現開始,從區域性迭代,逐步反思全域性設計,刻意保持低偶合,一個模組一個模組的來,再逐步嘗試組合。

自底向上強調先有實踐,再總結理論,理論反過來指導實踐,又從實踐中迭代修正理論。這和人類認識世界的順序是一樣的,先捕獵築巢,反思自然是怎麼回事,又發現可以生火,又思考自然到底怎麼回事情。

它的反面,是指大一統設計,你一開始用 UML畫出整套系統的類結構,然後再開工設計。這種思維習慣,如果是參考已有系統做一個類似的設計,問題不大,全新設計的話,他總有一個前提,就是 “你能完整認識整個大自然”,就像人類一開始就要認識捕獵和築巢還有取火一樣。否則每次對世界有了新認識,OOP的自頂向下設計方法都能給你帶來巨大的負擔。

所以有些人才會說:OOP設計習慣會依賴一系列設計靈巧的 BaseObject,然而過段時間後再來看你的專案,當其中某個基礎抽象類出現問題是,往往面臨大範圍的程式碼調整。這其實就是他們使用自頂向下思維方法,在逐步進入新世界時候,所帶來的困惑。

當然也有人批判這種強調簡單性和可拆分性的 Unix思維。認為世界不是總能保持簡單和可拆分的,他們之間是有各種千絲萬縷聯絡的,你一味的保持簡單性和可拆分性,你會讓別人很累。這裡給個藥方底層系統,基礎組建,儘量用 C的方法,很好的設計成模組,隨著你程式設計的積累,這些模組象積木一樣越來越多,而彼此都無太大關係,甚至不少 .c檔案都能獨立執行,並沒有一個一統天下的 common.h讓大家去 include,介面其他語言也方便。

然後在你做到具體應用時根據不同的需求,用C++或者其他語言,將他們象膠水一樣粘合起來。這時候,再把你的 common.h,寫到你的 C++或者其他語言裡面去。當然,作為膠水的語言不一定非要是 C++了,也可以是其他語言。 

備註:
這裡主要在探討 OOP存在的問題,並沒有討論嵌入式這種資源限制的情況,以及作業系統和底層等需要精確控制硬體和記憶體的情況,更沒有討論 C++在語言設計層面的事情。

轉部分答疑:(點選more展開)

Q:“實際上是,如果你能清晰的知道你要做什麼事情,那C很好。但如果你只能確定流程基本是對的,而很多系列可能在後續維護中不斷更改,或者增加更多的支援,那c的overhead就很大了。當工程非常龐大的時候會很難維護。比如開發一個數學演算法庫,其實跟數學沒有關係,就是一個大數(任意精度)的一系列計算程式。這個程式可以是沒有GUI的。開始自己設計一個大數的運算核心,然後還有很多更高階的計算演算法。將來有一天想把核心替換成GMP庫,或者使用者可以動態替換自己的核心”

A:就以你說的大數計算為例,大數計算底層驅動根據CPU更換函式指標是個不錯的選擇,見polarssl openssl,我還真建議你用C來寫大數的底層,因為你今天要算個求冪取模,明天要算個gcd,後天要生成質數,你無法預測你的大整數裡面究竟有多少個介面,這時候用c的分治思想就很合適。大數不是一輛飛機,它會飛,會降落,會拐彎,這都是飛機的主動行為,主動行為是有限的,確定的,適合oo的。而一個數字,它幾乎沒有啥主動行為,相反全是無限的,不可控的被動行為,正合適塞到不同的.c檔案中。這種時候你想刻意在一個大數類裡設定滿無限的方法是不合適的,不該oo的。況且你要跨語言,大數基礎庫用c介面到其他語言方便。所以你會看到openssl polarssl到其他語言的很多繫結,可你從來不會看到crypto++除了c++外被匯出到其它任何語言了。在你用C實現了大整數基礎功能並導給其它語言介面後,針對c++使用者,專門包個class的殼,選擇一些基礎方法放進去,給cpp使用者提供點方便。下面核心演算法變了,比如你實現了一個sse版本的乘法,執行時換函式指標即可,外層完全不可見,多好!polarssl中還用了一些巨集來代替為數不多的幾處用模版很方便的地方。