如何評論"知名遊戲開發者稱 C++ 是一種非常糟糕、可怕的語言"?

如何評論

C++ 的主要缺陷在於它是一個非常複雜和分層的生態系統,在解決不同問題的過程中變得越來越複雜 ; 層數越多,堆疊越高,變得越不穩定,理解起來就越難。

這個問題確實存在,但這個問題不是C++的問題,而是需求的問題:簡而言之,如果需求簡單的話,直接把C++當作帶類的C,甚至完全類都不用,有何不可?

反過來,用C實現稍複雜的需求,那就到處都是各種類型強轉,滿天都是函數指針,還有各種多重指針/指針數組——很多人看著雙重指針就已經一臉懵逼了,那麼去體驗一下經典的nginx的4重指針再來說C(和C++相比)不糟糕不可怕好嗎?

總而言之,真正可怕的是需求的複雜性,以及變更頻繁程度。

如果這兩者都相對簡單,那麼相對來說,用C確實會更輕便一點。但如果兩者都複雜,那麼實話說,大多數號稱精通C語言的開發者,寫出來的代碼大概率比C++更糟糕更可怕。

如何評論

C++不適合需求就換語言唄。非要把C++噴得一無是處就是矯情了。

已經有一批又一批的人嫌棄C++老土/難學/不夠酷,換了個離需求更遠的語言,最後要麼項目砸鍋,要麼哭著用C++重寫。

某人用掛城門的方式來要求別人尊重他人啊。你確實是不想吵架,你只是把反對Jonathan的人掛起來捱罵。我是不是對C++有宗教狂熱先不說,我看你對他起碼是有的。

我是沒Jonathan段位高,不過,他不應該只有虐我這個菜雞的水平吧。一個又一個的大3A,以及最主要的遊戲引擎都是以C++為核心的,Jonathan這種大神,大可以和這些作品的程序團隊成員去比比,看看是不是裡面的人個個都不如他。

C++毛病一大堆,Jonathan不是第一個噴的,也不是最後一個,更不是段位最高的一個。坑多,複雜,代碼容易寫醜陋,一些路走彎了,一些現代化的概念缺失,practice不好的情況下很難理解等等等等,C++社區都快討論爛了。無論是外部的d/go/rust還是內部革新的c++11/14/17,改良的重點都在這裡。Jonathan的話翻來覆去就沒什麼新意。

C++很依賴best practice,因為C++會提供大量屠龍之技,不打好招式基礎就濫用很容易走火入魔。濫用操作符重載之類的做法,基本上都是社區早就批倒批臭的bad example。

如果不樂意學best practice,怪語言?那就怪吧。Java也可以寫一個50個參數,一萬行的方法,C#可以unsafe滿天飛,Kotlin裡也可以使勁!!,沒人攔著你,對不對?

C++的確比別的語言更依賴best practice,因為C++信任你,認為你知道自己在做什麼,所以C++會盡可能多地提供特性,少進行強制性的約束,讓你的思維儘量不受語言功能的束縛。的確這會帶來非常多的坑、繁瑣的細節,醜陋的外觀等等,但相應的也是C++的優點所在:你確實需要某種功能/範式,而你的語言卻提供不了/只能用非常低效的辦法提供的情況,C++裡是最少的。

如果你只需要特定的一些功能,而且這些功能有比C++更優雅/更方便的語言可以實現,那就換語言啊。C++是拿來用的,不是拿來上供的。

至於Jonathan,矯情就是矯情,大神也會矯情,也會有缺點,有什麼好奇怪的呢?指出大神的問題來,就是不尊重人咯?

Jonathan確實無所謂C++,他能自己搞出個語言來,他有矯情的資本。然而多的是合適C++的項目,以及水平不如Jonathan的人。我們這些肉體凡胎跟著神仙騰雲駕霧只有摔死的下場,早點明白有什麼不好?

如何評論

個人觀點,C++ 功能超多,使用超靈活。這一點現在沒有哪個開發語言能與之媲美。

但是這一點也讓很多人害怕。

原因很簡單,C++ 純粹就是把能提供的都提供了。沒提供的,其實你也可以用提供的功能自己寫。

但是這會導致功能濫用。其實開發過程最糟糕的就是濫用功能。引入自己無法控制的內容,之後無法控制的內容再引入無法控制的內容而導致崩潰。

當然這也是我們會計最頭疼的事情,失控很影響管理和整個運作體系的正常。

所以我的觀點,凡是噴 C++ 可怕的,基本都是無管理公司的人員,以及根本不會管理也沒技術可言的公司/項目高層。

有些人把所謂的公司每天加班到幾點就叫做管理。但是其實真正的管理,都是管理代碼的佈局,前後關係,包括函數數量,類的上下級關係,包括變量都要有控制。程序員自己的代碼寫完了憑什麼不允許人家提前下班?

C++ 帶來的優勢,主要是可以更容易的分塊研發,從而實現大團隊整體研發的同步前進。但是團隊研發,每個人的任務之間如何協調,模塊之間的如何調用,才是真正的 C++ 研發精髓所在。

你餓了,要吃飯,四菜一湯頂天了吧?一桌子人,有十幾個菜也撐死了。所以你會噴滿漢全席幾百道菜讓你去醫院被醫生嘲笑吃撐了嗎?

如何評論

做遊戲開發用C++是挺難受的,可能很少有人體會過編譯一次客戶端要15分鐘以上的艱辛。現在大家用unity寫C#就舒服很多了,type convertor,reflection,attribute,async/await,lambda,event,extension這些好用的工具一堆一堆,我終於可以少加點班了(才怪)。

C++早就有functional和lambda,但我覺得這些東西都是“gateway drugs to functional programming languages”。

如何評論

c++不太是一個能增加程序複雜度的語言,不僅有社區提了很多年的zero overhead abstract,積累到現在的best practice[1]系列更是開發者們理解語言設計意圖,避免蹩腳造輪子的良藥。至於c++為什麼會成為這位遊戲開發者眼中的噩夢,我覺得可能有兩點:

  • c++畢竟是一門靜態語言,還是有一些靜態語言的原罪在裡面。以我個人的經驗來看,管理靜態和動態的邊界是個很讓我小心並且反覆設計的事兒,比如靜態多態和動態多態的分工合作,以及引申出的靜態 and 動態數據的劃分和組合。
  • 遊戲開發本身的程序複雜度擺在那裡。以我接觸過的各種語言來看,無論是偏靜態的純C、c++、java、c#也好,又或者是動態化腳本化的python、lua、js也好,針對邏輯的表述能力並不會有什麼不同,很多時候的爭論往往只是在扯:哪門語言的語法糖更適合我的口味:)( 好像在很多問題下我們已經證明了打字速度並不是影響開發效率的決定性因素,所以純C這種沒啥糖的語言也逃過一劫:) )所以以我有限的經驗來看,一門語言能為程序員服務的上限大概就是完全不增加邏輯複雜度了,想降低難度估計是不太可能。

至於隨著程序的複雜化出現的疊床架屋情況越來越嚴重,大概是這位開發者在OOP的道路上鑽了一些牛角尖所致,在我看來,如果能靈活運用cpp優秀的多範式支持能力,在coding中自由的排列組合的話,減少代碼的疊床架屋並非難事,比如我的整個降低複雜度的coding-style的演變流程是這樣的:

  • 基於oop做抽象,引入mvc mvvm 設計模式 解決 動態多態 && 單分派 處理不能或者比較噁心的問題。
  • 引入靜態多態,引入std::function, 基於從 @陳碩
  • 那兒看來的object-based-programming[2],把class抽象粒度砍成function抽象粒度,砍掉對應的設計模式。
  • 基於cpp-con 2014 && gdc 2017,引入dod,引入ecs(最近被遊戲圈說爛的一個架構哈哈:) )[3],解決邏輯回滾和undo-redo設計問題,把剩餘的class粒度砍成context粒度,降低了一些抽象化,平鋪了一批邏輯,同時由於引進了system的概念,算是和上一條一起解決了Multiple Dispatch的問題,算是正式砍掉了visitor模式。
  • (進一步的演變還在孕育中)
如何評論

在互聯網上,關於C++將要被市場所淘汰的討論從未停止過,有人說C++是一門過時的編程語言,也有人說,C++是不可替代的。那麼,C++過時了嗎?C++程序員會不會被淘汰?

這是我們客棧上的註冊C++程序員,大約2300多人,佔總人數的5%左右,這還不包括會C++但沒有標籤的,雖然我們都知道C++在走下坡路,但C++短時間內絕不會消亡!

如何評論

C++為什麼不會消亡?採訪了多名C++程序員後,得出以下三個觀點——

1、在遊戲和工具領域仍然是主流

首先是遊戲領域,Milo Yip表示——程序員必須使用C++框架/庫,如大部分遊戲引擎(如Unreal/Source)及中間件(如Havok/FMOD),雖然有些C++庫提供其他語言的綁定,但通常原生的API性能最好、最新。

其次是工具領域,無論是網絡安全還是殺毒軟件,C++仍是主流語言。

如何評論

程序員擅長語言統計 程序員客棧統計製作

2、C++程序員的收入沒有受到影響

根據100offer的後臺數據顯示,目前入職的程序員年薪最高達47萬,最低22.4萬,C++程序員的收入與其它編程語言的崗位相比處於持平狀態,沒有出現劣勢。

隨著C++逐漸成為某些特定企業和特定項目所需的語言後,高級C++程序員的收入也會更具有競爭性。

3、C++仍具有不可替代性

知乎紅人

@vczh

說:「我在上大學的時候幾乎就只學習C++,後來實習的時候去了微軟,結果到了那裡才知道,那個組是不用C++的,怎麼辦?憑藉著C++帶給我的殷實的功底,我按時完成了老闆給我的“兩個星期內學會C#和WCF基礎知識”的工作,順利開始工作。」當然,這只是vczh的個人經歷,不具有普遍性,但不可否認的是C++仍具有不可替代性。某家創業公司CTO在接受採訪時表示:「即便有很多人唱衰C++,但在當代,仍有很多很多項目的目標平臺暫時只提供C++編譯器的支持,僅從這一點而言,C++是不可能徹底死亡的。」

從應用領域來說,C++適用於高性能計算、嵌入式系統、開發服務器軟件、遊戲、實時系統等,所以,短期內能徹底取代C++語言並不存在。

C++在系統、圖形、網絡等很多領域都是不可替代的,它的光輝歲月讓它的死亡速度得以削減。

所以某知名遊戲開發者對C++的如此評論,只不過是對某種語言的鄙視吧,程序員業內不是有很多鄙視鏈嗎,比如我在北京四號線地鐵大喊:php是最好的語言,java是已經死掉的語言,產品經理是好人!

如何評論

要回答這個問題,就必須去了解Blow對於遊戲設計以及編程開發的基本觀念和認知。現在排在比較高位的幾個回答可以說都沒有答道點子上,也沒有理解Blow說這句話的目的和意味是什麼(我就不吐槽某個帶逛還瞎J8胡扯什麼word功能複雜的,那完全是牛頭不對馬嘴)

關於遊戲設計者Jonathan Blow

Jonathan Blow是《Braid》和《Witness》這兩款獨立遊戲的設計者。他早年就讀於加州大學伯克利分校,學的計算機專業。後來因為對學校中所學的東西感到沒有意義,就在畢業前一年選擇退學(當時就差那麼十來個學分來著)。

退學後,Blow作為一個程序員,先是在SGI幫忙移植過DOOM,且他負責過關於DOOM聯機部分代碼的移植工作(注意,那個時候還是90年代初)。此後,在96年,他退出公司和另一個朋友創業,成立了一家遊戲設計公司,他們負責開發一個叫做Wulfram遊戲。這個遊戲是一個3D的多人在線射擊類遊戲。儘管很多人會說,這種遊戲現在爛大街了,然而要注意,開發這款遊戲的時候還是90年代,當時網絡還很不發達,Blow和其它開發者必須解決如何在9600帶寬的網絡上實現實時的多人交互功能。雖然這個遊戲後來開發完成了,但因為90年代的遊戲環境還不太接受這類多人射擊類型的遊戲(過於先進)以至於最後沒能收回成本,公司也倒閉了。無奈之下,他有跑去其它遊戲公司繼續做開發工作,之後接受過各類遊戲開發,代碼審查和設計方面的工作,

如何評論

硬件機能被軟件反噬

說到這裡,我們需要明確一點,Blow和大部分現在的軟件開發者不同的是,他的工作經歷可以回溯到上世紀八九十年代。這除了說明他的開發經驗(尤其是C++語言方面)比在座的各位都要豐富之外,一個重要的特點是:像Blow這樣的老一代開發者在看待程序語言的時候,往往帶有歷史的延續性和大局觀。

和我們普通開發者不一樣,只是看了兩本人月雞湯或者XX Primer就上來吹逼什麼的。Blow在90年代初就參與過大型3D遊戲的開發工作,而他看到的是如今顯卡和系統硬件水平飛速提升的情況下,遊戲和軟件在某些方面的性能卻反而下降了。

之前聽雞盒王的廣播說,這個Blow曾經在一次演講上諷刺PhotoShop打開一個圖片居然要花費超過一秒的時間。因為人家曾經在90年代就開發過只需要運行在9600帶寬網絡上的在線多人射擊遊戲了,為何如今在硬件性能如此大幅度提升的條件下,軟件的性能水平居然下降了(我不禁想到WIN10更新的那個硬盤驅動,讓7200轉的機械硬盤都能卡的半死,其技術“水平”也是可見一斑的)。

究其原因,其實無它,就是因為軟件世界本身一直在退化。軟件開發的過程和效率正變得越來越依賴於已有的API庫和架構不良,極度複雜的第三方框架。就好像你用JAVA去構造一個簡單的Android上的HELLOWORLD頁面,結果它能生成幾十個你都不知道從哪裡多出來的類和代碼。這種高度的冗餘性和奇葩的複雜性導致了軟件運行性能,尤其是軟件的開發效率大幅度下降。注意,是軟件開發效率。

試想一下,如果你是一個真的在拿C++開發大型3D遊戲的開發者,那麼這一過程中所要涉及到的諸如接口的調試,外部數據配置,垃圾對象回收,系統移植和兼容性等問題就夠你吃一壺了。這對於那些小眾遊戲的開發者,尤其是獨立遊戲的開發者,他們團隊往往人數不多,生產力有限,因此不可能像許多3A大廠那樣去流水線式的大規模工程開發。因此諸如C#的Unity以及python上的一些遊戲框架就很受歡迎,為什麼?因為這些編程語言和遊戲引擎在配置和使用的接口描述方面要遠遠比C++這種近底層的語言簡單優雅的多得多,而且更加的節約開發成本。

如何評論

Blow的解決方案--程序語言JAI

當你瞭解了C++由於自身語言設計以及那令人蛋疼的系統庫複雜度之後,就應該會理解它對於遊戲開發所帶來的開發效率問題了。

所以才有了後來Jonathan Blow提出要自己開發一個新的遊戲編程語言--JAI,來取代現有的主流遊戲編程語言C++。Blow對於JAI的基本定位是包含了下面幾個方面

  • high performance:支持生成程序的高性能
  • joy of programming:讓學習者產生對編程的興趣
  • simplicity:簡潔的程序語法和直觀的表現力
  • low friction:程序結構間的衝突和耦合儘量減少
  • designed for good programmers:為優秀的程序開發者所準備,只有對如何將程序的直觀表述與所要設計的遊戲理念優雅結合起來的程序員才能理解這一語言的魅力。

這些定位有好幾個顯然是針對C++做的改良,首先,能讓學習者產生編程興趣是讓他們將自己所要開發的遊戲理念轉換成現實產品的第一步;而C++顯然缺乏這個特性。試想在學習了C++ Primer一個月後,你的同行還在和你爭論如何實現一個C++的垃圾回收器,那麼這個語言在你們的項目中就是一個失敗的選項。

其次,簡潔性和直觀性,這也是針對C++極度冗餘,複雜難堪的程序庫接口和語言使用方式的一次改進。由於C++過度的接近底層,以至於當你想要使用一個在其它語言上非常常見的特性時,你得

“自己實現”它!這就增大了從程序實現到表達意圖的成本。

作為一個例子,我曾經用C++開發過一個(自己閒著蛋疼)運行在GNU-C99規範的編譯器前端。當我試圖用它的面向對象特性去設計語法樹類型節點的時候,驚訝的發現C++沒有類似於Java中的instanceof這樣的動態判斷關鍵字。究其原因,是因為C++的底層內存模型並沒有包含對象的“動態類型信息”,這導致當我要去解釋每個抽象語法樹節點的時候,還是得根據語法樹自帶的枚舉類型關鍵字來判斷,結果是這種實現就退化成了C的風格了(所以說我當初是為什麼要用C++來著,算了這都不重要了 =A=)。

試想一個類似的情形,在開發遊戲的過程中,當用戶希望將一個對象隨機生成它的某個子類的對象實例,來增加遊戲的多樣表現力的時候,他們發現C++居然無法識別動態類型信息,於是這種時候,就只好去藉助與自己在class中定義metatype的枚舉信息,然而,每次當他們想增加一個新的遊戲對象的時候,就得不斷的同時增加這個metatype的關鍵字以及對應的上次處理模型代碼,這實際上就造成了系統模塊間的耦合度。

所以這就說回來了一個問題,為何C++是一個可怕的語言。因為用C++實現的許多系統,在邏輯複雜性和代碼規模上都過分的高,以至於難以讓小眾的遊戲開發團隊掌控,而且他們為了學習這個複雜語言所消耗的精力卻讓他們沒有精力去做那些他們真正想要做的事情。

如何評論

Blow的遊戲系統設計理念:直觀與簡單

這裡就得談到Jonathan Blow的人生哲學了。雖然我個人沒太去了解過這貨的履歷,也沒讀過多少篇他的博客。然而我在聽過雞盒網的那幾個節目以及他在核聚變現場演講後,我一直有個感覺,我覺得這個人對遊戲的看法,是一種對極簡和直觀的追求。

例如Blow曾經在演講時抨擊很多現有的遊戲要素是高度人為化的。什麼意思呢,就是刻意設計出來讓玩家去察覺和使用的。他認為這種刻意的設計削弱了遊戲系統本身的可玩性。例如育碧裡的沙盒遊戲,多數是集中在如何增加地圖尺寸和敵人數,進攻點防守點等固定的人為設計的要素,來構造遊戲性。然而這就導致除了遊戲給你的這些人為設計的要素外,你沒有什麼其它可玩的。你想看看水下有沒有什麼新世界?對不起,那只是個貼圖。遊戲設計者已經把所有要給你的都呈現給你了,剩下的你也看不到。

所以這樣的系統是非常刻意的,使得玩這些所謂的開放世界久了,會讓人產生乏味感。因為核心原因在於,所有的要素都在一開始被人為塑造好了,你沒有什麼可以去“進一步發現”的東西了。所以你就自然而然的覺得沒有什麼新意了。3A大廠的下一款開放世界遊戲?我猜應該還是那些套路,奪點,佔地,幹boss。你已經知道了一切,你也就失去了一切。

而相反,如果你玩過Braid和Witness,你就會發現,那兩個遊戲裡面,玩的不是可以設計出來的某些要素,而是一個遊戲的系統原型,等待玩家自己去探索。而這種探索的感覺給玩家的就是一種“真實”:因為我們的現實世界就是這樣的,你永遠不知道現實世界會發生什麼,會存在什麼,在太陽系外面還會有什麼東西,在你看不到的地方發生了什麼。這些就是真實,換句話說,真實就是未知的驚喜,而人為刻意構造出來的東西,給人的感覺就是一種虛假。一個真實的世界應該是提供無限的信息(可能性),而一個人造的世界只會提供有限的信息(可能性)。

如何評論

C++的設計缺陷:過分刻意的設計要素

所以在Blow的遊戲哲學裡,我能很明顯看到他對於自然的,直覺的東西的讚美以及對那些刻意設計出來的要素的鄙視。所以我們現在談下C++。

C++這個語言的設計問題在哪?用一個最簡單的Blow式的評價就是:整個語言除了C的內核外,全是刻意設計出來的。例如std::cout << xxx,設計出來就只是為了讓“流處理”這個數據讀寫操作看上去更方便。而為了讓這個方便,C++直接把<

知道為何學習C++是一件讓人痛苦的事情嗎?因為在整個C++對C進行擴展的部分中,都能看到大量十分刻意的語言結構設計,這些設計其實只是為了滿足非常“特定的”語言特性和使用習慣,而這些習慣往往不是必要的!

這就導致很多人學習C++必須去為了那些他們可能這輩子都用不到的被刻意設計出來的特性花費大量時間學習,而且你根本不知道你應該怎麼用它們以及你到底會不會用它們!

如果我們把C++比作一個遊戲,那麼這個遊戲系統充斥了極度複雜且不知所云的數值特性系統,這些數值被刻意設計出來,而大多數人根本用不到,以至於很多人一上手這個遊戲的時候就基本上決定退坑了!

所以說為何Blow會決定為了提升遊戲的穩健性和開發效率,去直接設計一款新的遊戲編程語言。因為C++這樣一個違反了人的認知和描述世界的基本理性原則,到處都是刻意設計要素的語言要素和語法結構,以至於不斷干擾著開發者們將更多的精力投入到開發更好的遊戲和產品中去。

這也是為何現在很多小作坊做獨立遊戲幾乎全TM是在用Python或者C#。

如何評論

個人的一點感想

我本人也在百度和阿里做過幾年的開發工作,主要內容是系統底層網絡模塊方面的,就C++這個語言,我個人覺得是設計十分失敗的。它一直妄圖為C增加所謂的面向對象特性,然而在這設計方面表現的過於刻意,以至於當使用者用它們實現具體功能的時候,還得需要大量的trick,這就導致我們得分散精力去解決代碼的問題,而不是集中精力去解決需求和功能設計的問題。

這就回到了幾個高票回答說的:C++沒有問題,有問題的是需求。

你們發現了嗎?這不就等於變相承認C++在處理和實現某些過分複雜的需求和用戶意圖時會增加大量的開發成本了嗎?

雖然我本人不認為就編程語言發動一場宗教審判有什麼意義。但我認為,一個語言的設計者應當把自己當作一個開發“新世界”要素的設計師。如果你要描述的世界過於複雜,那麼就把這個複雜性交給世界的觀察者(或使用者)讓他們自己去發掘,而不是自己去想出這個世界的所有複雜要素,然後把它們刻意的添加到編程語言裡面。就像某些遊戲把遊戲中所有的要素都刻意設計出來,最後剝奪了玩家探索這個世界的權力一樣。

而據我所知,C++就是這樣一款充斥了大量刻意要素的語言。它的設計者非常希望讓C++“極具表現能力”,然而他們的實現方式卻是向語言裡添加過多極度刻意的語法要素,這就導致了很多不必要的麻煩。

我一直認為,作為一個開發者,程序語言應該能夠直觀的表述程序員和用戶所要實現的功能和意圖,除此之外,它應當足夠簡介。而C++這款語言在這方面就是完全的反例。刻意的增加了大量的不必要的語法要素,導致學習和開發成本高速增加,最後寫出來的程序和C基本上差別不大(甚至你用C寫出來說不定還更短)。

當然,我所指的也只是我所在的項目開發團隊這幾年裡開發經驗的一個感受。有的C++大神確實刻意駕馭這個語言(也許有吧)。但是作為一個水平一般的普通開發者,我是不會在萬不得已的條件下(例如要去維護某個之前用C++寫的系統),去用C++開發新的軟件的。如果一個新的項目即將立項,要選擇編程語言,我的意見是,選擇那個能夠最直接最簡潔最容易描述你所要實現目的的語言,用它先做出一個alpha版,然後在有條件的情況下,再去用主流的系統程序語言(例如Java或C++)去實現它的穩定和高性能版本。

這樣做的好處是,當一切功能都已經清晰明瞭且接口明確的時候,你才有精力去應付代碼的問題。在此之前,一切工作的核心應該以“有效實現產品功能”為前提。

如何評論

問題1:刻意設計語法範式的弊病

首先,C++引入了很多的語法特性,然而這些特性都是非常刻意被設計出來的,並固定成為一種普適的語法範式。這就導致了很多問題。比如C++為了能良好地支持複數的計算以及流處理,為+和<

這導致了一個問題,這些被作為“普遍適用”的語法特徵卻只有很少真正適合使用它們的場景,這就造成要麼這個語法範式在大多數情況下用不到,要麼大多數情況下使用它們的開發者都是非常不恰當的在使用它們!

我就見過一些初學者真的按照C++ Primer那本書上的商品對象加操作對諸如遊戲對象player重載過+操作,結果我看了半天發現,那個player1 + player2執行的只是兩個玩家的經驗做了加法,而其它屬性一概不變。一個開發者如果看到這段代碼,還以為是疊加了Player的所有屬性呢!為什麼,因為+操作在除了數值和字符串外的其它複雜對象上往往是“意義不明”的。這種重載操作的濫用導致很多代碼的穩健性維護有問題。

有人會說吧,那是你程序員自己亂用重載操作符啊。沒錯!但是如果開發者不“濫用”這些重載操作的話,那麼重載操作這個語法特性就基本上不會被使用了!而且對於經驗豐富的程序員來說,a + b與a.add(b),add(a, b)是沒太大區別的。完全沒有必要為了讓代碼看著更COOL而去使用不同規範的a + b來表示某種對象操作。

C++裡面充斥了很多這種刻意設計出來,以滿足特定使用場景的語法特性。而在特性完全可以在JAVA和C這樣的語言上以方法和函數的形式實現,而且代碼行數也不會更多,性能也不會差多少。這就造成C++ Primer那本書介紹了一大堆的語法結構,最後使用的上的其實還只是個C with classes(說實話,我在百度工作很長時間,絕大部分的代碼,新的或者舊的,都是C with classes的實現模式,其它IT公司不清楚),這些完全沒必要的語法特性,除了增加學習成本,還有就是提高理解和維護系統的複雜性了。這也是為什麼C++ is horrible的原因。

如何評論

問題2:看似刻意實則無用的語法特性

另一個重要的問題,表面上,C++提供了許多語法特性來加速開發流程,實際上它們起到的作用是非常少的,而且有些特性與其說是為了支持諸如多樣性開發,不如說是完全的累贅。

我舉個例子,例如在寫一個解釋器的過程中,需要將程序轉換成抽象語法樹(AST),然後根據AST的語法樹節點類型來解釋和計算表達式的值。那麼大家覺得在這個系統中,類型AstNode和操作compute的關係是什麼呢?

很多在C++上開發的程序員習慣認為compute是AstNode的成員函數:

class AstExpression : public AstNode {
/* compute the value of this expression in given environment */
AstValue * compute(Scope &, Environment &);
}

然而這實際上是一個不好的設計規範!因為AST只是表達式的表現(representation),而compute則是一個針對這一表達式的操作。換言之,compute是一個對於AstNode的外部操作,而不是它本身的屬性。所以比較正確的類結構應該定義成下面這個樣子:

class AstExpression : public AstNode {}; 

class AstInterpreter {
void set_environment(Environment &);
AstValue * compute(AstExpression &);
}

這樣的編碼有一個好處,那就是如果下一次,你在使用AstNode的時候需要添加對這一對象新的使用需求的時候,就不需要往上面一點點加操作了,而且這可以將表達式的“表示”和對錶達式的“理解”兩個過程分離到兩個模塊中進行。

試想一個支持多種計算和使用模式的AstNode類型其實是非常重的:

class AstExpression : public AstNode {
AstValue compute_in_ast(Environment &);
AbsValue abs_interpret_as(Environment &);
ConcreteValue concrete_interpret(Environment &);
// other ways to use this node are ignored...
}

這也導致當你使用這一模式的時候C++的一個類的代碼可以達到上千行。因為根據我古歐的開發經驗來看,這種重型類都是沒有做好功能分離,在設計初期沒有將使用功能和屬性功能分離清楚,導致設計急速膨脹的結果。

那麼好。在我們分離了表達式的表示AstNode和計算Compute之後,就出現了另一個問題:表達式的計算需要根據表達式的類型來決定需要計算的方式,例如一個加法計算和一個比特位移運算並不一樣,於是:

AstValue * Computer::compute_arith_binary_expression(AstExpression & expr) {
if( is_arith_binary_expression(expr) ) {
/* interpret the expr here */
}
return nullptr;
}
AstValue * Computer::compute_shift_binary_expression(AstExpression & expr) {
if( is_shift_binary_expression(expr) ) {
/* interpret the expr here */
}
return nullptr;
}

然而這裡有一個問題,那就是對於一個父類AstExpression,C++並不支持對該父類的動態類型檢測,C++雖然定義了所謂的語法typeof,然而這個玩意兒其實只能返回靜態類型,而並不知道動態條件下對象expr的真實子類。

原因呢?因為C++的內存模型是繼承自C的,而C的內存模型中並不提供自動維護對象類型信息的部分。因此用C++做這個問題,就需要用戶手動標記一個類型在AstExpression內部,類似於這樣:

class AstExpression : public AstNode {
AstExprType etype;
}

然而這種設計就回歸到了C語言的狀態了。所以對於C++開發者來說,最簡單的方法就是直接把compute操作插到AstExpression類裡面。而這種操作,就如同我們前面所說的,是面向對象設計中非常忌諱的,它最終會導致模塊間高度冗餘和系統耦合度增大:

class AstExpression : public AstNode {
/* I finally decide to add this method
to the node class, because this is
the fastest way to implement the
polymorphism of computation on AST */
AstValue * compute(Environment &);
}

當然技術比較好的開發者會使用其它開發模式,諸如讓每個類型返回一個類頭,或者提前開發一個對象類型的管理系統,以及在計算器一旁通過一個基於該類型系統的分類器來自動定位對象類型。然而這就導致代碼的邏輯複雜度大幅度提高。使得本來很直觀簡單的compute這樣一個業務需求變得高度冗餘。

很多組裡的同事經常吃飯開玩笑說,C++裡面使用的那些編程模式,其實是把非常簡單的問題變得拐彎抹角的實現了。這就解釋了為何大多數人寫出來的C++代碼冗餘,複雜度高,且變現力差的原因了。也解釋了我前面提到的:為了表達某個簡單的需求,C++需要使用非常複雜且結構耦合的代碼,最終導致開發工作異常困難。

同樣的功能,在JAVA裡面實現就非常自然:

AstValue compute_arith_binary_expression(AstExpression expr){
if(expr instanceof AstArithBinaryExpression) {
/* TODO ... */
}
}

這就是為何有人會調侃C++的多態性是一種被動多態,而非主動多態。因為它很多時候需要用戶提供額外的類型信息給外部使用者來實現一些在其它語言上其實非常簡單的功能。

如何評論

問題3:所謂直觀性與簡潔性

Blow在遊戲設計中強調過系統設計需要儘可能簡潔,而不是拐彎抹角的用一大堆人為加工痕跡的東西去增強遊戲。這種簡潔並不是意味著簡單,例如Blow設計的遊戲第一次玩起來都很玄學,完全搞不懂。他所說的簡潔性很大程度上是一個基於簡單的幾個要素和規則架構起來的系統原型,而不是像其它遊戲那樣不斷提升貼圖質量和動作感官精細度的方式增強遊戲的方式。

這個思想在編程裡面也是適用的。程序是程序員用來向計算機表達用戶需求和功能的媒介。語言在其中扮演著重要的角色。一個好的語言應當能夠直接闡述用戶的意圖,同時讓程序員易於實現它的表現結構。而C++的許多(近乎惡劣的)設計卻是這一目的的反例。

諸如我們提到的靜態多態性和刻意增加的操作符重載等,都是一種降低表現直觀性,增加實現複雜度的做法。而這些做法的設計“意圖”卻是非常刻意的,而這種刻意的語言設計哲學,在我個人看來,就和Blow的簡潔直觀的系統設計哲學是背道而馳的。它使得我們得拐彎抹角的去迎合那些所謂的“C++精英程序員”以及它們的設計者的意圖,而實際上卻增加了我們自身開發學習的難度。更糟糕的是,這種迎合特定編碼習慣的方式,使得我們很難維護好所要設計系統和實現間的橋樑,造成諸多麻煩,以至於開發者沒有足夠的精力去開發實現真正值得他們解決的問題和需求。

這也導致另一個有趣的結論:不是C++太複雜了,而是需求太複雜了。

所以C++針對不適合開發高度複雜的大規模系統(所以你設計它出來幹嘛呢???)

建議:尊重其他程序員

有很多這種對程序語言抱著近乎宗教崇拜態度的人,他們凡是看到有人批判某個語言在某方面不好,就開始嚷嚷對方是個弱智。可惜的是, @farta

恕我直言,這位對C++大做批判的遊戲設計者不僅有幾十年的C++開發經驗,而且人家成功的用C++實現了許多3D遊戲的設計和移植工作,其技術力遠比向您這樣的噴子來的強。那些批判某個語言的人,不一定是不會這門語言的初學者,也可能是已經身經百戰,根據自己多年遊戲開發的項目經驗所作出的合理判斷,而Blow屬於後者。

大多數情況下,是一批C++狂魔看了兩個月C++ Primer後,就開始充滿熱情開始了某個大型系統的項目,最終在幾年後,我們高興地發現對方用Python實現了該項目的所有基本功能目標:)

如何評論

這個問題並不在於C++如何如何,而是在於這位“知名遊戲開發者”是誰。

如果你是遊戲文化愛好者,你應該知道這個名字——Jonathan Blow(喬納森·布洛)

如何評論

被稱為“大神”的人有很多,但是如果把“大神”的標準定義為:堅定的理想主義者,人類探索未來文化的殉道者。那麼能被稱為“大神”的人就屈指可數了,Blow可能是其中的一位。

人物與作品簡介

Jonathan Blow生於1971年,是一位美國的遊戲設計師和開發者。他最知名的作品是《時空幻境》(Braid,2008)與《見證者》(The Witness,2016)。

(注意他的年齡已經與DOOM之父喬恩·卡馬克相仿,和現在的90後比是老幾輩的遊戲開發者。他在獨立遊戲界出名之前也做了很多那個時代遊戲開發工作。)

《時空幻境》

本作品開始於2004年,是一部以時間倒流為靈感而開始的作品。2005年12月放出了最初的簡陋版本,這個版本贏得了2006年獨立遊戲節(Independent Games Festival)最佳遊戲設計獎。

之後他繼續完善遊戲的表現部分,直到2008年8月在Xbox Live上推出了正式版。推出時,Blow個人債務達4萬美元,已投入20萬美元用於遊戲的開發。

作品推出後受到批判性的讚揚,且在經濟上也大獲成功;這部作品在媒體Metacritic上獲得93%的好評,成為Xbox Live Arcade平臺上最高好評率的遊戲。

在這之前已經有過不少獨立遊戲,但是本作標誌性地讓“獨立遊戲”獲得口碑與市場雙重豐收,所以也有人將這部作品的問世記為“獨立遊戲元年”(非公認)。

《見證者》

本作在2009年宣佈開發,也是Blow到目前最近的一部作品。它的遊戲舞臺是一個小島,是一個主視角解謎遊戲。本作品同樣是由Blow自己出資開發的,據報導投入為2~3百萬美元。

儘管被普遍看做解謎遊戲,但是Blow並不認為“解謎”是這個遊戲主要表達的意思,而且他也不可能做一款單純的“解謎”遊戲。

Blow將他在前作《時空幻境》中所有的利潤都重新投入到了《見證者》當中,且在後期同樣也背上了債務。為了提升遊戲的歷史感和人文遺蹟的真實性,Blow聘請了專業的景觀設計團隊來為遊戲設計了普通玩家難以察覺到的、帶有深刻內涵的建築與花園。

這部作品在時隔多年後的2016年1月26日推出,被IGN評為滿分10分、也獲得了商業上的成功。但是,本作超前的設計理念令其受到極大的爭議。任何一個遊戲玩家都可能毫不猶豫地向朋友推薦《時空幻境》,但是對《見證者》會持保留態度。這部遊戲對部分玩家來說屬於“神作”,但是更多人會在體驗到這個遊戲的結局之前放棄。

其他成果:

2010年3月,Blow聯合多位知名獨立遊戲開發者,成立了Indie Fund(獨立遊戲基金會),他本人也成為了一名獨立遊戲的天使投資人。

2012年,在著名紀錄片《獨立遊戲大電影》中,Blow作為主角之一,與觀眾分享了他的遊戲開發經歷和思想。

★ JAI 語言

(終於和編程語言問題沾邊了。)

2014年9月,Blow開始著手開發一門新的編程語言稱為“JAI”,他打算總結他在遊戲開發實踐中積累的經驗,開發一門聚焦於遊戲製作的全新語言。這門語言的目標是達到“低阻力的流式開發”,支持面向數據設計以達到高性能。目前這門語言還在開發當中,且還沒有發佈可運行的編譯器。

有趣的是,儘管關注的人不多,但是Blow會在Youtube等平臺上長時間地介紹他的這門新語言的設計理念與技術細節。

Blow的哲學與觀點

Blow的遊戲設計理念,以及其遊戲哲學,在國內是被遊戲文化媒體“機核”廣泛傳播的。機核先後做過多期關於喬納森·布洛的視頻、電臺節目與專題,且在2018年線下大型聚會“核聚變”中也請到Blow本人發表演講,主題為“遊戲是人類思想的未來”。

Blow曾在媒體上多次發表過他對於遊戲設計的一些觀點,這些觀點往往帶有極強的個人色彩,難免偏激。比如他認為遊戲中“吃金幣”的設計就是比較低級的,只是討好玩家的手段。這種低級的設計不尊重玩家作為“人”的一面。

他的充滿爭議性的觀點,也得到了大部分玩家的讚美,被粉絲稱為是“正義的、電子遊戲革命所需要的”和“一個靈魂探索者,在未知的境界中追求真理。”( "the kind of righteous rebel video games need"[22] and "a spiritual seeker, questing after truth in an as-yet-uncharted realm."[3])

Blow常說,遊戲具有更大的潛力。他試圖讓更多成年人更多地關注到遊戲世界之中,遊戲有可能在未來社會中扮演一個越來越重要的角色,但是現在的遊戲開發者還遠未察覺到這一點,只是在關注一些低風險、高收益的主題。另外,Blow之前獲得過物理學位,他表示遊戲可以像物理學那樣,對我們的宇宙做出實驗。

目前對喬納森·布洛哲學思想的解讀,很多時候存在過度解讀的嫌疑,對其觀點讀者應當帶著自己的思考去理解,謹防陷入主觀情感上的崇拜(也毫無意義)。

布洛的本人是他自己的哲學思想的踐行者,這點毋庸置疑。所以對他的言行也不必過於敏感,比如他說“C++是一種非常糟糕、可怕的語言”。事實上,C++這門語言顯然有很多優點也有很多缺點,使用C++的人應當自行做出判斷。

如何評論

作為一門語言在遊戲中是否優秀,很多時候取決於是否能滿足各式各樣的需求。遊戲架構的多樣化遠高於其他軟件的架構,無論是服務端還是客戶端。比如我們這裡需要用到面向數據的開發,面向數據的開發適合許多大場景,大體量,實時運算量大但運算量多樣化相對較小的遊戲,如使命召喚的單機模式。但是,請稍等一下,假設我們這裡使用的是C++,但是我們偏偏要用動態化的函數式編程來使其面向數據,但是我希望我的函數是“乾淨的”,所以使用模板生成的function首先不在我的考慮範圍內,我確確實實需要一個乾淨的事件,但是大多數情況下他都是一個非純函數,那麼情況可能就會變成以下這樣(C++熟手輕噴,請原諒如此拙劣的代碼):

如何評論

首先,這段代碼是否高效?毫無疑問是的,純淨的函數指針,非純的函數並只訪問指針內的字塊,首先它是緊湊的,如果我想用數組儲存一串Func::f,遍歷執行他們也是高效的,其次,C++底層靈活的內存控制使其對CPU的緩存非常友好,這也和數據的緊湊性離不開關係,C++精確的static_cast也能給編輯器提供優化的方向,使其對緩存更加友好,一碼歸一碼,在多線程異步中也有較好的穩定性。但是請稍等,這段代碼為何看起來如此噁心?我們先看一段類似的C#代碼在Unity3D 2018和.Net4.x中的操作:

如何評論

顯然,實現同樣的效果,同樣的效率(使用IL to Native C的C# code,在上述的各方面實際與C++代碼非常接近),而這次我可以快快樂樂的喜提action到一個總管的類裡了。

這樣可能並沒什麼說服力,我當然可以使用靜態純函數,像ECS架構一樣,方便快捷,不用非要這麼極端吧,但是遊戲開發中確確實實有許多這樣“極端的例子”,一個語言的強大在於,他需要考慮各種古怪不開竅的開發團隊或個人開發者,比如我。


分享到:


相關文章: