C++ 與複雜性文化

oschina發表於2013-09-17

  閱讀前提:

  注意:這邊文章是供軟體開發專家欣賞,而並非供GreaterThanZero的使用者欣賞。

  閱讀者需要熟悉C++並且至少掌握一門其它物件導向程式語言。

  概要:

  在我的印象裡"複雜性文化"深深植根於C++社群裡。在這片文章裡面,我將帶領大家探索C++歷史,以期從中理解這種現象。

  我是一個C++的長期使用者,並且作為一名作者和鼓吹者在C++社群裡面活躍了若干年。是的,GreaterThanZero的後端大量使用了Java而非C++。這是否意味著我加入了浩浩蕩蕩的反C++大軍?不,不是這樣,我能給個證明:搜尋“C++ auto and decltype",你就會發現第一個結果就是我幾周前寫的一片文章。用Java實現GreaterThanZero的數學後端,是基於一些其它方面的考慮,主要是因為需要使用Google App Engine作為釋出平臺。

  每當我給GreaterThanZero寫JavaScript或者Java程式碼的時候,我經常發現我自己在思考這個問題,“哈,so easy.媽媽再也不用擔心我的程式碼了。”接著,我不得不慚愧的承認,我很失望。因為沒我沒有機會證明自己比別人更聰明。我不得不去安慰自己說,我實現的數學不簡單呢。

  想要給人們留下你的程式碼極其複雜的印象而不是你的軟體,是一種不夠成熟且工程做的不好表現。我就幹過這種事。無需多言,這是我需要克服的。我不會將我自己的缺點和人格弱點怪罪他人。但是,我覺得如果說想要給別人留下程式碼是複雜的印象的這種壞習慣只能在一種“複雜性文化中”才能誕生並且流行起來,在這種文化中,複雜以及難以理解的程式碼雖然未必得到承認和鼓勵,但是至少是可以忍受的。雖然我不知道這有多流行,但從我自己的經驗來看,這種複雜性文化是C++社群讓人備受折磨的一部分。

  我相信你若要解決一個難題,就必須首先了解其根源。所以,我將通過追溯C++和麵向物件程式設計的根源,來嘗試解釋在一些C++社群中會碰到的對複雜文化的偏好現象。

  廣為傳播的共識中,第一種面嚮物件語言是Simula 67。早期的為OO模式的出現做出貢獻的開發工作發生在MIT的List領域的工作,特別是在人工智慧組。我不清楚Simula 67的發明者多大程度上受到了MIT的工作的影響。然而,有意或無意的,Simula 67採用了引用語法(reference semantics)以及gb(garbage collection)特性(後面詳細敘說),這的確顯示對Lisp的一些延續。

  那C++又是如何參與進來的呢?正如這篇文章: Wikipedia article on Simula中所說:“C++的創始人, Bjarne Stroustrup認為在開發C++過程中,為了把Simula具有的提高開發效能的特性和底層語言如BCPL所具有的原生的計算速度特性相結合,Simula 67對他的影響最大。

  公平的說,可以認為Simula 67和OO模式的出現是一種創新的,如果不算革命性的,觀念從已有的有效實踐中誕生的例子。我所說的Lisp使用了引用語法( reference semantics )和gb特性的事實就是證據。我認為,C++把OO模式和高效性以及C具有的底層控制能力相結合的方法是另一種形式的努力。為得到最好的效果,把其他兩個完全獨立的特性包含進來,在我眼裡這是一種實驗。我這樣說不是表達批評之意,相反,我相信人類的好奇心和無休止的對更優方案的追求將使得這種實驗得到有效執行。

  C++的實驗並沒有失敗,那麼多的人用C++寫出了那麼多的令人驚訝的有用軟體足以打破任何人所宣稱的C++失敗的言論。另一方面,就我而言,C++卻也並非完全成功了,要知道,有人會說,這似乎很有魅力,似乎OO特性和C的底層控制都是為對方而準備的。在我看來,有很多很明顯的不一致和不協調,它們有兩種展示自己的方法:

  1. C++程式設計師不得不花費大量工作到no other end,而不是使語言工作。

  2. C++的進步是得到了一個處理語言核心的不一致和不協調性的影響的外部驅動。

  要得到上面說法的證據,考慮除了C++和Objective C的OO程式語言,想想Java和C#,下面的場景:

x = y;

  這樣會發生什麼?它使得變臉x引用了y引用的物件,並且讓垃圾回收器處理上面的表示式引起的對x之前引用物件的影響。這就叫 reference semantics,這也發生在函式引數傳遞時。

  現在看看C++中相同的行,此時假設x和y都是使用者自定義型別的物件。這樣的行都做了些什麼呢?是的,在我們沒有看到使用者定義型別的原始碼的時候,我們當然不知道做了什麼,而且複製賦值操作符還可能出現過載。然而有預設行為,一般來說,預設的行為就是x指向的物件和y指向的物件具有同樣的狀態。技術上來說,狀態相同是通過這種方式獲得的:物件的複製賦值操作符可以遞迴地應用到物件的成員,直到物件的成員是基本型別比如int為止,這時,將會進行正常的舊的值賦值。這也稱作值語義,然而我更願意稱其為狀態語義。物件是沒有值的,它們只有狀態,所以在這裡發生了這樣的轉換行為。

  大多數人都同意變數之間賦值是程式語言設計的一部分。在C++裡,使用者自定義型別的變數之間賦值涉及到物件拷貝。不過經驗表明複製物件很困難。下面從comp.lang.list的常見問題解答(FAQ)頁面的摘錄對此進行了很好的總結:“問:為什麼這種程式語言沒有深度拷貝功能? 答:拷貝任意結構或者物件需要根據上下文來確定正確的拷貝應該怎麼做(哦,原來是這樣!)。”在C++裡,對物件的拷貝就會遇到例如把C風格的指標當作類成員看待這樣的技術問題,這樣的事實使得物件之間的拷貝更加難以實現。我相信,有證據支援我上面的 猜想1:C++程式設計師不得不更加努力地確保變數之間的賦值功能將像基本型別那樣正確地執行。這裡僅僅只有一個例子;我將準備給出更多這樣的例子。

  為了尋找支援上面我的猜想2的證據,我請你看看rvalue引用。Rvalue引用給出了物件移動語義,而且rvalue引用解決了完全轉發問題。對使用者自定義的型別使用引用語義和垃圾回收的古典物件導向的語言既沒有移動語義所關注的效能問題,也沒有完全轉發問題。C++自己正在解決這兒提到的問題,而且它已經按照對應用程式設計人員不透明的方式這麼做了:rvalue引用使得應用程式設計人員可以看到更多的語言介面。再者,這只是對我猜想的提供證據的一個例子;我將提供更多這樣的例子。

  據說一個公司或者組織都有自己的DNA。如果程式語言也有自己的DNA是真的話,那麼對我來說,C++也應該內建了自己的DNA。

  • 希望關注程式語言內部的問題,而且語言內部的設計會大大地影響到程式設計人員日常生活裡。
  • 更加關注實驗、更加關注功能所取得的即刻好處,而問題則留給以後在修改。
  • 可以稍稍增加程式語言的體積以及提供給使用者的介面這種傾向,而很少關心功能之間互動所產生的混合複雜性。

  另外,不要為出於虛榮心而編寫的複雜的並且實驗性的程式碼這樣不可原諒的行為請求原諒,我真正感覺到誘使我這麼做的是一種文化,這種文化好的一面是根本不關心如何如何的複雜,壞的一面是鼓勵增加複雜性。我相信:哪些要求使用者懂得許多並且讓使用者有更多的的機會狂熱地進行試驗性程式設計和複雜性程式設計的語言管理者應該有責任在建立讓語言簡化的文化中付出更多努力。說實話,我根本看不到這樣的努力。站在像Go語言這樣的競爭者的角度來看的話,我對C++的未來很悲觀!

  原文地址:http://blog.greaterthanzero.com/post/58482859780/c-and-the-culture-of-complexity

相關文章