C++ 的反思

該文章來自用戶轉載 點擊閱讀原文

  最近兩年 C++又有很多人出來追捧,並且追捧者充滿了各種優越感,似乎不寫 C++你就一輩子是低端程序員了,面對這種現象,要不要出來適時的黑一下 C++呢?呵呵呵。咱們要有點娛樂精神,關於 C++的笑話數都數不清:
1、笑話:C++是一門不吉祥的語言,據說波音公司之前用ADA爲飛機硬件編程,一直用的好好的,後來招聘了一夥大學生,學生們說我靠還在用這麼落後的語言,然後換成C++重構後飛機就墜毀了。
2、笑話:什麼是C++程序員呢?就是本來10行寫得完的程序,他非要用30行來完成,並自稱“封裝”,但每每到第二個項目的時候卻將80%打破重寫,並美其名曰 “重構”。
3、笑話:C容易擦槍走火打到自己的腳,用C++雖然不容易,但一旦走火,就會把你整條腿給炸飛了。
4、笑話:同時學習兩年 Java的程序員在一起討論的是面向對象和設計模式,而同時學習兩年 C++的程序員,在一起討論的是 template和各種語言規範到底怎麼回事情。
5、笑話:教別人學 C++的人都掙大錢了,而很多真正用 C++的人,都死的很慘。
6、笑話:C++有太多地方可以讓一個人表現自己“很聰明”,所以使用C++越久的人,約覺得自己“很聰明”結果步入陷阱都不知道,掉坑裏了還覺得估計是自己沒學好 C++。
7、笑話:好多寫了十多年 C++程序的人,至今說不清楚 C++到底有多少規範,至今仍然時不時的落入某些坑中。
8、笑話:很多認爲 C++方便跨平臺的人,實際編寫跨平臺代碼時,都會發現自己難找到兩個支持相同標準的 C++編譯器。
—————
Q:那 C++爲什麼還能看到那麼多粉絲呢?
A:其實是因爲 Windows,因爲 Windows的興起帶動了 C++,C++本來就是一門只適合開發 GUI的語言。

Q:爲何 C++只適合開發 GUI呢?
A:你看 Unix下沒有 GUI,爲啥清一色的 C呀?所有的系統級問題都能在 C裏找到成熟的解決方案,應用級問題都能用其他高級語言很好地解決,哪裏有 C++什麼事情呀?

Q:你強詞奪理,Unix下也有 C++的項目呀。
A:有,沒錯,你任然可以用任何語言編寫任何糟糕的代碼。

Q:別瞎扯了,你都在說些什麼?連C++和 Windows 都扯到一起去了。
A:回想下當年的情景,一個大牛在教一羣初學者如何編程。一邊開發一邊指着屏幕上說,你看,這是一個 Button,我們可以用一個對象來描述它,那是一個 panel我們也可以用一個對象來描述它,並且你們有沒有發現,其實 Button和 Panel是有血緣關係的,你們看。。。這樣就出來了。。。。下面的學生以前都是學着學校落後的教材,有些甚至還在用 turboc的 bgi庫來畫一些點和圓。哪裏見過這麼這麼華麗的 Windows 界面呀。大牛說的話,象金科玉律一樣的銘刻在自己幼小的心理。一邊學着 Windows,一邊發現,果然,他們都需要一個基類,果然,他們是兄弟關係,共同包含一些基本屬性,可以放到基類去。他們越用越爽,潛意識裏覺得因爲 C++這麼順利的幫他們解決那麼多界面問題,那看來 C++可以幫他們解決一切問題了。於是開發完界面以後,他們繼續開發,當他們碰到各種設計問題時,反而認爲肯定自己沒有用好 C++。於是強迫自己用下去,然後就完蛋了。
—————
  關於 C++的笑話我有一籮筐,各位 C++粉用不着對號入座。言歸正傳,爲什麼要黑 C++呢?談不上黑不黑,我從94年開始使用 C++(先前是 C 和 Pascal),一路看着 C++成長壯大,用 C++寫過的代碼,加起來應該超過 10MB了吧,C++的各種寶典我也都讀過,一直到 2004年開始切回 C,主要原因是發現很多沒法用 C++思路繼續解決下去的問題,或者說用 C++思路解決下去會很糟糕的問題。
  那時候(2004-2005)正是 C++滿天飛的時候,言必稱 C++,用必用模版,我跳出來說你們醒醒吧,別過火了,這個世界並不是都是抽象數據結構和算法就可以描述清楚的。於是很多人激動的跳出來說:“你沒領會到 C++精髓,你根本都不會用 C++”。我問他們:“語言是用來解決問題的,如果一個語言學了三四年都會經常掉溝裏,算好語言麼?如果編寫十多年 C++的程序員都很難掌握得了,這算好語言麼”。他們又說:“語言是死的,人是活的”。
  我記得當時一位國內 C++大牛,爲了糾正我的 “錯誤觀點”,給我看過他寫的一套十分強大的庫,我打開一看,倒吸了一口冷氣,全部是 .h文件。我只能回他三個字:“你牛逼”。當然這是一個極端的例子,那傢伙後來終於也開始把 .h裏面的東西逐步挪到 .cpp裏面了,這是好事。
  當時和雲風在一家公司,2004年新人培訓時,他給新人佈置了一個實現內存分配器的作業,批改作業的時候,他經常邊看邊問人家,“不夠C++呀,你能不能百分之百OOP?”,“1%的 C都不要留”。我當時在公司內部郵件列表裏面發過關於 C++的問題,大部分人都表示:“你看沒有C++我們怎麼寫3D引擎呢?”。我跟他們講:“John Carmack直到 Quake3都還在用着 ANSI C,後來因爲不得不支持 D3D,改用 C++了。爲啥 C不能寫 3D引擎了?”。他們告訴我:“你看,Point,就是個對象,Matrix也是個對象,那麼多 Vector的代數計算,用 C++的算術重載是多麼美妙的事情,三維世界就是對象的世界。”。
  確實當時客戶端 GUI的話,只有 C++,圖形引擎也只有 C++,這兩個正是C++最強的地方,所以我也沒和他們爭辯,強迫他們承認 C也可以很漂亮的寫圖形,而且C寫的可以寫的很優雅。我又不是閒着沒事情,何必去質疑人家的核心價值觀呢,呵呵。當年我正在接手一個 C++項目,代碼超過 800KB,每次崩潰都需要花費很長時間去定位,項目中大量的前後依賴,改一個地方,前後要看好幾處,一處遺漏,整個系統就傻逼了。我開始重構後,畫了兩個星期,將性能敏感的核心部分剝離出來用 C實現(代碼量僅 200KB),然後導出 Python接口,用Python來完成剩下的部分,整個腳本層代碼量只有 150KB。整個世界清爽了,整個 C++項目原來的工期爲 2個程序員四個月,我一個人重構的時間加起來就 1.5個月,而且代碼量比遠來少了兩倍還多,各種奇特的 BUG也一掃而盡。我看看左邊的 800KB一團亂麻的 C++代碼,再看看右邊整潔的 300多 KB 純 C + Python,琢磨着,這個項目幹嘛不一開始就這麼做?

一、跨語言接口
  現代項目開發,不但需要更高的性能,而且需要更強大的語言描述能力。而 C++正處在一個尷尬的地方,比底層,它不如 C能夠精確的控制內存和硬件,各種隱式構造讓你防不勝防;比描述能力,比快速業務開發和錯誤定位,它又趕不上 Python, Ruby, Lua等動態語言,處於東線和西線同時遭受擠壓和蠶食的地步。
很快,2006-2007年左右,其他項目組各種濫用 C++的問題開始顯現出來:當時腳本化已經在工程實踐中獲得極大的成功,然而某些項目一方面又要追求 100%的 C++,另一方面又需要對腳本導出接口,他們發現問題了,不知道該怎麼把大量的 C++基礎庫和接口導給 Lua。
  C的接口有各種方便的方式導給腳本,然而整個項目由一羣從來就不消於使用腳本的cpp大牛開發出來,當他們要吧cpp類導出接口給腳本時,他們設計了一套牛逼的系統,lua自動生成機器碼,去調用c++的各種類,沒錯,就是c++版本的cffi或者ctypes。他爲調用vc的類寫了一套機器碼生產,又爲調用gcc的類寫了一套代碼生成。那位cpp大牛寫完後四處炫耀他的成果,後來他離職了,項目上線一而再再而三的出現無可查證的問題,後來雲風去支援那個項目組,這套盤根錯節的c++項目,這套盤大的代碼自生成系統深深的把他給噁心到了。後來衆所周知雲風開始反C++,倡導迴歸C了,不知道是否和這個項目有關係。
  於是發現個有趣的現象,但凡善於使用腳本來提高工程效率的人,基本都是C加動態語言解決大部分問題(除了gui和圖形),但凡認爲c++統治宇宙的人很多都是從來沒使用過腳本或者用了還不知道該怎樣去用的人。
  憑藉這樣的方法,我們的產品同競爭對手比拼時,同樣一個功能,同樣的人力配置,競爭對手用純C++要開發三月,我們一個月就弄出來了,同樣的時間,對手只能試錯一次,我們可以試錯三次。後來,據我們招聘過來的同事說,競爭對手也開始逐步降低 C++的比例,增加 java的比例了,這是好事,大家都在進步嘛。

二、ABI的尷尬
  ABI級別的 C++接口從來沒有標準化過,以類爲接口會引入很多隱藏問題,比如內存問題,一個類在一個庫裏面實例化的,如果再另外一個庫裏面釋放它們就有很多問題,因爲兩個動態庫可能內存管理系統是不一樣的。你用這裏的 allocator分配一塊內存,又用那裏的 allocator去釋放,不出問題纔怪。很多解決方法是加一個 Release 方法(比如 DX),告訴外面的人,用完的時候不要去 delete,而是要調用 Release。
  項目寫大了各個模塊隔離成動態庫是很正常的,而各種第三方庫和自己寫的庫爲追求高性能引入特定的內存管理機制也是很正常的。很多人不注意該調用release的地方錯寫成delete就掉溝裏去了。更有勝者跨 ABI定義了很多inline方法的類,結果各種隱式構造和析構其實在這個庫裏生成,那個庫裏被析構,亂成一團亂麻。C就清晰很多,構造你就調用fopen,析構你就fclose,沒有任何歧義。其實C++的矛盾在於一方面承認作爲系統級語言內存管理應該交給用戶決定,一方面自己卻又定義很多不受用戶控制的內存操作行爲。所以跨 ABI層的c++標準遲遲無法被定義出來,不是因爲多態 abi複雜,而是因爲語言邏輯出現了相互矛盾。爲了彌補這個矛盾,C++引入了operator new,delete,這new/delete重載是一個補丁並沒從邏輯上讓語言變得完備,它的出現,進一步將使用者拖入bug的深淵。
  其實今天我們回過頭去看這個問題,能發現兩個基本原則:跨abi的級別上引入不可控的內存機制從語言上是有問題的,只能要靠開發者約定各種靈巧的基類和約定開發規範來解決,這個問題在語言層是解決不了的;其次你既然定義了各種隱式構造和析構,就該像java活着動態語言一樣徹底接管內存,不允許用戶再自定義任何內存管理方法,而不是一方面作爲系統極語言要給用戶控制的自由,一方面自己又要搶着和用戶一起控制。
  因此對象層 ABI接口遲遲無法標準化。而純 C的 ABI不但可以輕鬆的跨動態庫還能輕鬆的和彙編及各類語言融合,不是因爲C設計多好,而是C作爲系統層語言沒有去管它不該管的東西。當年討論到這個話題時 C++大牛們又開始重複那幾句金科玉律來反駁我:“語言只是招式,你把內功練好,就能做到無招勝有招,拿起草來都可以當劍使,C++雖然有很多坑,你把設計做好不那麼用不就行了”。我說:本來應該在語言層解決好的事情,由於語言邏輯不完備,將大量問題拋給開發者去解決極大的增加了開發者的思維負擔,就像破屋上表漿糊一樣。你金庸看多了吧,武術再高,當你拿到一把槍發現子彈不一定往前射,偶爾還會往後射時,請問你是該專心打敵人呢?還是時刻要提防自己的子彈射向自己?

三、系統層的挫敗
C++遭受挫敗是進軍嵌入式和操作系統這樣靠近硬件層的東西。大家覺得宇宙級別的編程語言,自然能夠勝任一切任務,很快發現幾個問題:
· 無法分配內存:原來用 C可以完全不依賴內存分配,代碼寫幾千行一個 malloc沒有都行。嵌入式下處理器加電後,跳到特定地址(比如起始地址0),第一條指令一般用匯編來寫,固定在0地址,就是簡單初始化一下棧,然後跳轉到 C語言的 start函數去,試想此時內存分配機制都還沒有建立,你定義了兩個類,怎麼構造呀?資源有限的微處理器上大部分時候就是使用一塊靜態內存進行操作。C++寫起來寫爽了,各種隱式構造一出現,就傻了。
· 標準庫依賴:在語言層面,C語言的所有特性都可以不用依賴任何庫就運行,這爲編寫系統層和跨平臺跨語言代碼帶來了很方便的特性。而C++就不行,我要構造呀,我要異常呀,你爲啥不能給我強大的運行時呢?什麼你還想用 stl?不看看那套庫有多臃腫呀(內存佔用,代碼尺寸)。
· 異常處理問題:底層開發需要嚴格的處理所有錯誤返回,這一行調用,下一行就判斷錯誤。而異常是一種鬆散的錯誤處理方式,應用層這麼寫沒問題,系統層這麼寫就很狼狽了。每行調用都try一下和 C的調用後if判斷結果有什麼區別?C++的構造函數是沒有返回值的,如果構造內部出錯,就必須逼迫你catch構造函數的異常,即便你catch住了,構造異常的時候當然會自動觸發相關內部對象的析構,但是有很多並沒有析構的資源(比如系統資源,比如C接口的資源,他們都沒有一個析構),整個過程是很難控制的,此時這個實例是一個半初始化實例,你該怎麼處理它呢?於是有人把初始化代碼移除構造函數,構造時只初始化一下變量,新增加一個帶返回的init函數,這樣的代碼寫的比C冗餘很多。何況硬件中斷髮生時,在你不知道的情況下,同事調到一些第三方的庫,你最外層沒有把新的exception給 catch住,這個exception該往哪裏拋呀?內存不夠的時候你想拋出一個 OutOfMemoryException,可是內存已經不夠了,此時完全無能力構造這個異常又該怎麼辦呢?
· 處理器兼容:C++的類依賴基地址+偏移地址的尋址方式,很多非 Intel系列的微處理器上只有簡單的給定地址尋址,不支持這樣一條語句實現BASE+OFFSET的尋址,很多C++代碼編譯出來需要更多的指令來運算地址,導致性能下降很多,得不償失。
· 隱式操作問題:C的特點是簡單直接,每行語句你都能清楚的知道會被翻譯成什麼樣子,系統會嚴格按照你的代碼去執行。而用C++,比如 str1 = str2 + "Hello" + str3; 這樣的語句,沒幾個人真的說得清楚究竟有多少次構造和拷貝,這樣的寫法編寫底層代碼是很不負責任的,底層需要更爲精細和嚴格的控制,用C語言控制力更強。
當然,說道這裏很多人又說,“C++本來就是 C的超集,特定的地方你完全可以按照C的寫法來做呀。沒人強迫你構造類或者使用異常呀”,沒錯,按 Linus的說法:“想要用 C++寫出系統級的優秀的可移植和高效的代碼,最終還是會限於使用 C本身提供的功能,而這些功能 C都已經完美提供了,所以系統層使用 C的意義就在於在語言層排除 C++的其他特性的干擾”。很多人都記得 Linus在 2007年因爲有人問 Git爲什麼不用 C++開發炮轟過一次C++。事實上2004年 C++如日中天的時候,有人問 Linux內核爲何不用 C++開發,他就炮轟過一次了:實際上,我們在1992年就嘗試過在Linux使用 C++了。很噁心,相信我,用C++寫內核是一個 “BLOODY STUPID IDEA”。事實上,C++編譯器不值得信任,1992年時它們更糟糕,而一些基本的事實從沒改變過:
·  整套 C++異常處理系統是 “fundamentally broken”。特別對於編寫內核而言。
· 任何語言或編譯器喜歡在你背後隱藏行爲(如內存分配)對於開發內核並不是一個好選擇。
· 任然可以用 C來編寫面向對象代碼(比如文件系統),而不需要用 C++寫出一坨屎來。
總得來說,對任何希望用 C++來開發內核的人而言,他們都是在引入更多問題,無法象 C一樣清晰的看到自己到底在寫什麼。
C++粉絲們在C++最火熱的時候試圖將 C++引入系統層開發,但是從來沒有成功過。所以不管是嵌入式,還是操作系統,在靠近硬件底層的開發中,都是清一色的 C代碼,完全沒有 C++的立足之地。

四、應用層的反思
  STL出來後,給人一種 C++可以方便開發應用層邏輯的錯覺。由於很多語言層不嚴密的事情,讓STL來以補丁的方式完成,於是很多以爲可以象寫 java一樣寫 C++的初學者落入了一個個的坑中。比如 list.size(),在 Windows下vc的 stl是保存了 list的長度的,size()直接 O(1)返回該變量,而在gcc的 stl中,沒有保存 list長度,size()將搜索所有節點,O(n)的速度返回。
  由於語言層不支持字符串,導致 std::string實現十分不統一,你拷貝構造一個字符串,有的實現是引用,才用 copy-on-write的方法引用。有的地方又是 new,有的實現又是用的內存池,有的實現線程安全,有的實現線程不安全,你完全沒法說出同一個語句後面到底做了些什麼(見孟巖的《Linux之父話糙理不糙》)。
  再比如說我想使用 hash_map,爲了跨平臺(當你真正編寫跨平臺代碼時,你很難決定目標編譯器和他們的版本,想用也用不了 unordered_map),我很難指出一種唯一聲明 hash_map的方法,爲了保證在不同的編譯器下正常的使用 hash_map,你不得不寫成這樣:
#ifdef __GNUC__
#ifdef __DEPRECATED
#undef __DEPRECATED
#endif
#include
namespace stdext { using namespace __gnu_cxx; }
namespace __gnu_cxx {
template<> struct hash< std::string > {
size_t operator()( const std::string& x ) const {
return hash< const char* >()( x.c_str() );
}
};
}
#else
#ifndef _MSC_VER
#include
#elif (_MSC_VER < 1300)
#include
#define IHAVE_NOT_HASH_MAP
#else
#include
#endif
#endif
#ifdef __GNUC__
using namespace __gnu_cxx;
typedef hash_map HashXXXX;
#else
using namespace stdext;
typedef hash_map HashXXXX;
#endif
  如果有更好的跨平臺寫法,麻煩告訴我一下,實在是看不下去了。一個基礎容器都讓人用的那麼辛苦,使得很多 C++程序員成天都在思考各種規範,沒時間真正思考下程序設計。
  由於語言層要兼容 C,又不肯象 C一樣只做好系統層的工作,導致當 C++涉足應用層時,沒法接管內存管理,沒法支持語言層字符串,沒法實現語言層基礎容器。所以需要藉助一些 stl之類的東西來提供便利,但 stl本身又是充滿各種坑的。且不說內存佔用大,程序體積大等問題,當編譯速度就夠嗆了。所以爲什麼 C++下面大家樂意重複造輪子,實現各種基本容器和字符串,導致幾乎每個不同的 C++項目,都有自己特定的字符串實現。就是因爲大家踩了坑了,纔開始覺得需要自己來控制這些細節。stl的出發點是好的,但是隻能簡單小程序裏面隨便用一下,真是大項目用,stl就容易把人帶溝裏了,所以很多大點的 C++項目都是自己實現一套類似 STL的東西,這難道不是違背了 stl設計的初衷了麼?
  語言層的缺失,讓大家爲了滿足業務開發的快速迭代的需求,創造了很多很基礎的設計靈巧的基類,來提供類似垃圾回收,引用計數,copy-on-write,delegate,等數不勝數的功能。每個項目都有一系列 BaseObject 之類的基礎類,這樣就引入一個誤區,兩年後你再來看你的代碼,發現某個 BaseObject不滿足需求了,或者你和另外一個項目 merge代碼時,需要合併一些根本屬性。圖形和GUI這些萬年不變的模型還好,應用類開發千變萬化,一旦這些設計靈巧的基類不再適應項目發展時,往往面臨着全面調整的代價。
  打開一個個 C++大牛們 blog,很多地方在教你 std::string的原理,需要注意的事項。map的限制,vector的原理,教你如何實現一個 string。這就叫 “心智負擔”,分散你的注意力,這是其他語言裏從來見不到的現象。戰士不研究怎麼上前線殺敵,天天在琢磨搶和炮的原理,成天在思考怎麼用槍不會走火,用炮不會炸到自己,這戰還怎麼打?
  所以此後幾年,越來越多的人開始反思前兩年C++過熱所帶來的問題,比如高性能網絡庫 ZeroMQ作者 Martin Sustrik 的:《爲什麼我希望用C而不是C++來實現ZeroMQ》,比如雲風的《雲風的 BLOG: C 的迴歸》,比如引起熱議的《Why C++ Is Not "Back"》。

五、全面被代替
  2008年以後,行業競爭越來越激烈,正當大家一邊苦惱如何提高開發效率,一邊掉到C++的各種坑裏的時候,越來越多的應用開發方案湧現出來,他們都能很好的代替 C++。各行各業的開發者逐步相見恨晚的發現了各種更加優秀的方案:需要底層控制追求性能的設計,大家退回到 C;而需要快速迭代的東西大家找到各種動態語言;介於性能和開發速度之間的,有java,知乎上好像很多黑java的,語言是有不足,但是比起C++好很多,沒那麼多坑,真正考慮面向對象,真正讓人把心思放在設計上。所以再黑也不能擋住 java在 tiobe上和 C語言不是第一就是第二的事實,再黑也擋不住 java在雲計算,分佈式領域的卓越貢獻。
所以2005年以後,C++處在一個全面被代替的過程中:
· 底層系統:進一步迴歸 C語言,更強的控制力,更精確的操作。
· 網頁開發:2006年左右,C++和 fastcgi就被一起趕出 web世界了。
· 高性能服務:varnish, nginx, redis 等新的高性能網絡服務器都是純C開發的。
· 分佈式應用:2007年左右, C++被java和其他動態語言徹底趕跑。
· 遊戲服務端:2008年後進一步進化爲 C 和 腳本,完全看不到胖C++服務端了。
· 並行計算:2010年後,go, scala, erlang;而能方便同go接口的,是 C不是C++。
· 遊戲引擎:沒錯 C++和腳本,但是這年頭越來越多的開源引擎下,引擎類需求越來越少。
· 遊戲邏輯:腳本
· 多媒體:SDL純C,ffmpeg是純 C,webrtc的核心部分(DSP, codec)是純C的。
· 移動開發:早年C++還可以開發下塞班,現在基本被 java + objc + swift 趕跑了。
· 桌面開發:Qt+Script, C#等都能做出漂亮的跨平臺界面。且界面腳本化趨勢,不需要C++了。
· 網頁前端:JavaScript, Html5, Flash
· 操作系統:FreeBSD, Open Solaris, Linux, RTOS, Darwin(OS X 底層),都是純 C
· 虛擬技術:qemu / kvm (雲計算的基石)純 C,Xen 純 C
· 數據庫:MySQL (核心純C,外圍工具 C++),SQLite 純 C, PostgreSQL / BDB / unqlite 純C
· 編譯器:C/C++並存,不過編譯器用腳本寫都沒關係,我還在某平臺用 java寫的 C/C++編譯器
· 大數據:kafka, hadoop, storm, spark 都使用 Java / Jvm 系列技術
· 雲存儲:openstack swift python, hdfs java, 還有好多方案用 go
  可以看出,即便 C++的老本行,GUI和圖形(確實也還存在一些短期內 C++無法替代的領域,就像交易系統裏還有 COBOL一樣),這年頭也面臨的越來越多的挑戰,比如新發布的 Rust (如何看待 Rust 的應用前景? – 知乎用戶的回答)。可以發現,開發技術多元化,用最適合的技術開發最適合的應用是未來的趨勢。而爲這些不同的技術編寫高性能的可控的公共組件,並輕鬆的和其他語言接口,正是 C語言的強項。所以不管應用層語言千變萬化,對系統級開發語言C的需求還是那麼的穩定,而這個過程中,哪裏還有 C++的影子呢?

六、話題總結
  所以說未來的趨勢是:C x 各種語言混搭 的趨勢,從TIOBE上 C++的指數十年間下跌了三倍可以看出,未來還會湧現出更多技術來代替各個角落殘存的C++方案,C++的使用情況還會進一步下降。所以題主問學習純C是否有前途,我覺得如果題主能夠左手熟練的掌握 C語言,培養系統化的思維習慣和精確控制內存和硬件的技巧;右手繼續學習各種新興的開發技術,能夠應對各個細分領域的快速開發,碰到新問題時能左右開弓,那麼未來工作上肯定是能上一個大臺階的。至於C++ 嘛,有時間看看就行,逼不得已要維護別人代碼的情況下寫兩行即可。

七、故事分享
  古代用弓箭進行遠距離攻擊時,對射手要求較高,瞄準難度大,需要一直使勁保持準心。戰鬥中一個弓箭手開弓二十次就需要比較長的休息時間。弩的威力遠勝於弓,秦弩的製造就如現代的自動步槍一般精密無二,它既可以延長射擊,又可以精確瞄準。弩箭的發射速度更是弓箭的數倍,威力驚人。因爲弩的操作非常簡單,不需要射擊技巧,平民很容易掌握它的使用方法。秦國靠着弩兵,在戰爭中取得了不少優勢,被人稱爲 “虎狼之師”。
  日本投降時,天皇下罪己詔。很多士兵不願意相信這時真的,找種種理由拒絕相信。有的士兵甚至以爲天皇的廣播是敵人誘降的把戲,於是躲到叢林裏繼續三五成羣的收集情報,襲擊可以攻擊的目標,等待上司來給他們下達新命令。直到好幾年後看到周圍的人都穿着日常的便裝了,而來巡山的 “敵人” 也從士兵變爲了巡邏隊,他們都還覺得這是敵人的僞裝。而同時,德國戰敗時,最後的黨衛軍一直戰鬥到 1957年才肯投降。
—————————————–
  很多人覺得 java慢,C++快java 10倍以上已經是上世紀的事情了,現代的 java 只比 C/C++慢 70%,C++連1倍都快不了 java。也不要覺得動態語言慢,javascript只比C/C++慢 2.7倍。luajit只比 C++慢 5.8倍。在 jit技術發展的今天,C++在性能上離動態語言/java的差距越來越小,可易用性和生產效率上的差距,卻和動態語言/java 比起來越來越大。
C++ 的反思
————————— 
最後,補充一張圖:
C++ 的反思

  加入GAD的核心用戶QQ羣:484290331,各類活動獎勵任你拿,最新資訊任你讀,衆多教學任你免費學,如此好地方趕緊加入吧!另VR專屬羣:476511561,專業VR技術分享,專業導師指導爲你答疑解惑,大型小型活動獎勵等你拿,免費學習賺獎勵的天地,歡迎你加入喲!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章