你好,我是吳詠煒。
C++ 可算是一種聲名在外的編程語言了。這個名聲有好有壞,從好的方面講,C++ 性能非常好,哪個編程語言性能好的話,總忍不住要跟 C++ 來單挑一下;從壞的方面講,它是臭名昭著的復雜、難學、難用。當然,這樣一來,熟練的 C++ 程序員也就自然而然獲得了“水平很高”的名聲,所以這也不完全是件壞事。
不管說 C++ 是好還是壞,不可否認的是,C++ 仍然是一門非常流行且非常具有活力的語言。繼沉寂了十多年,并終于發布語言標準的第二版——C++11——之后,C++ 以每三年一版的頻度發布著新的語言標準,每一版都在基本保留向后兼容性的同時,提供著改進和新功能。本專欄主要就是講這些新特性以及相關的編程實踐。
在講所有這些細節之前,我想先講一講為什么要用 C++,什么時候該用 C++,及如何學習C++。
### C++ 的意義
C++ 程序員應該都聽到過下面這種說法:
> C++ 是一門多范式的通用編程語言。
多范式,是因為 C++ 支持面向過程編程,也支持面向對象編程,也支持泛型編程,新版本還可以說是支持了函數式編程。同時,上面這些不同的范式,都可以在同一項目中組合使用,這就大大增加了開發的靈活性。因此,C++ 適用的領域非常廣泛,小到嵌入式,大到分布式服務器,到處可以見到 C++ 的身影。
下面是一些著名的用到 C++ 的場合:
* 大型桌面應用程序(如 Adobe Photoshop、Google Chrome 和 Microsoft Office)
* 大型網站后臺(如 Google 的搜索引擎)
* 游戲(如 StarCraft)和游戲引擎(如 Unreal 和 Unity)
* 編譯器(如 LLVM/Clang 和 GCC)
* 解釋器(如 Java 虛擬機和 V8 JavaScript 引擎)
* 實時控制(如戰斗機的飛行控制和火星車的自動駕駛系統)
* 視覺和智能引擎(如 OpenCV、TensorFlow)
* 數據庫(如 Microsoft SQL Server、MySQL 和 MongoDB)
有些同學可能會覺得,這些應用場景似乎和平時的開發場景有點遠啊!你的感覺是對的。有些傳統上使用 C++ 的場合現在已經不一定使用 C++,最典型的是個人電腦上的桌面應用。以前 Windows 下開發桌面應用常常用 MFC,微軟的 C++ 框架,而現在我估計聽說過 MFC 的程序員都不多吧。目前很流行的 Visual Studio Code 主要是用TypeScript 寫的,不是 C++。而我自己也用 C# 寫過桌面應用,不過界面邏輯之外的計算和處理仍然是用一個 C++ 的 DLL 來完成。典型情況是,需要性能的組件用 C++ 來寫,整個應用程序融合多種不同的語言。
前面我提到了, C++ 的傳統領域有被侵蝕的風險,那是因為和它相競爭的語言遠遠不止一個,可以說是上下夾攻。
* 如果專注性能和最小內存占用的話,C 仍然是首選——嵌入式領域用 C 非常多,而Linux 也是用純 C 寫的。
* 如果專注抽象表達和可讀性的話,那 Python 之類的腳本語言則要方便得多。
* 圖形界面(GUI)編程傳統上是 C++ 的地盤,但近年來 C# 和 JavaScript 占領了很大一部分市場。
* 游戲算是 C++ 的經典強項了,但有了 C++ 寫的游戲引擎,游戲用 C# 寫也沒啥問題了——你可能不一定知道,Unity 游戲引擎上的首選開發語言是 C#,而王者榮耀是用什么游戲引擎呢?答案正是 Unity——所以王者榮耀可以認為是用 C# 開發的。
* 還有,Go 和 Rust 也加入了戰團,對 C++ 形成了一定的競爭……
### 看起來,C++ 有點危險啊……
不過,真是這樣嗎?我們需要回到 C++ 的核心競爭力上來看一下。
* 抽象能力:意味著較高的開發效率,同時,更重要的是,不會因抽象而降低性能。
* 性能:這不用多說了,就是快并且占用資源少。
* 功耗:這是近年來我們越來越關注的問題,跟性能直接相關,性能好了功耗自然就低
計算機在發明的初期,價格奇高,而性能拿今天的標準來看卻是極低的,自然不能不關注性能。慢慢地,計算機的性能“足夠”了,性能似乎也就不那么重要了,腳本語言于是也有了用武之地。而隨著移動設備的普遍使用,大量設備用電池供電而不接電源了,功耗就逐漸成了我們大家關注的大問題。因此,即使主流移動平臺的開發語言不是 C++——而是 Java和 Objective-C 或 Swift——但任何性能要求高的應用,都幾乎必然會用到 C++ 開發的組件。
同時,移動設備要聯網,也大大刺激了服務器的增加。在服務器端,雖然沒有電池電量的問題,但有著服務器集群的供電問題、空調問題、需要的服務器數量問題等,因而 C++ 的使用也是非常廣泛的。
前面說到了王者榮耀的客戶端是用 Unity + C# 開發的,但我沒有說王者榮耀的服務器端——那可還是用 C++ 開發的。另外,有一點我前面還藏著呢!雖然王者榮耀初期是純用Unity 開發的,沒有用到 C++;但后來,騰訊又用C++ 把游戲的邏輯部分獨立成了一個GameCore,進一步提高了性能 [1]。
目前,跟 C++ 定位差不多、能有直接競爭關系的,也就是既支持高度抽象、又追求高性能的通用編程語言,其實只有 Rust 一種。而 Rust 遠沒有達到跟 C++ 一樣的成熟和普及程度。這也可以從 TIOBE 的排名看出來:C++ 是第 4 位,而 Rust 是第 25 位 [2]。
另外,和 C 的兼容性,也是 C++ 的一大優勢。雖然現在很多大型程序都混雜了多種語言,但在小項目里,減少語言的數量可以簡化開發和部署。前不久,我在 Python 里做了一些加解密運算,發現使用的第三方庫性能仍不夠高,雖然它已經用了 C 開發的加解密引擎。所以,我找了用 C 寫的高性能加解密代碼,然后使用 pybind11 庫 [3],只手寫了一百來行的 C++11 代碼,就把性能又提高了幾倍。
### 什么時候該用 C++?
如此說來,C++ 既然性能又好,又支持抽象,為什么沒有更流行呢?
因為代價更高。C++ 是一種復雜的語言,難以上手和熟練掌握,因此也是一種比較容易出錯、被誤用的語言。C++ 一直與 C 基本保持了向后兼容性,這種兼容性,也一直是 C++的安全性和易用性方面的負擔。C++ 比起 C 來,要更安全,更不容易出現緩沖區溢出這類漏洞,但跟沒有指針概念的語言比起來,它仍然是一種“不安全”的語言。我的個人經驗,完成同樣的功能,C++ 需要的代碼行數一般是 Python 的三倍左右,而性能則可以達到Python 的十倍以上。
#### 問題來了:你在開發上額外付出的時間,能從性能上省回來嗎?
顯然,這取決于你開發軟件的用途和開發時間。舉個例子,如果你用 Python 開發需要一天,運行需要十秒,并且不需要反復運行;那么,轉用 C++ 開發就意味著開發費用也許要增加兩倍,開發加運行的總時間增加兩天,大虧。
反之,如果用 Python 開發還是需要一天,單次運行需要十秒,但是軟件會作為服務長時間運行、每天被調用十萬次。在這種情況下,明顯你就需要多臺服務器來支撐其使用了。這時,如果用 C++ 開發會需要額外的兩天,但跟 Python 相比,部署上有望節約十分之九的硬件和電費——那就很值了。
簡言之,當你的軟件屬于運算密集或者內存密集型,你需要性能、且愿意為性能付出額外代價的時候,應該考慮用 C++,特別在你的代碼需要部署在多臺服務器或者移動設備的場合。反之,如果性能不會成為你開發的軟件的瓶頸,那 C++ 可能就不是一個最合適的工具。
此外,在嵌入式應用的場景,那就根本不是值不值、而是行不行的問題。如果程序完成一個功能不能在指定的若干毫秒、甚至微秒內完成,那產品根本是失敗、不可用的。在這種場合,能和 C++ 競爭的只有 C,但 C 是一種開發效率更低、更需要堆人力的語言了。在嵌入式開發使用 C++ 的最大障礙可能不是技術,而是人力資源——搞嵌入式開發的程序員可能大多都習慣使用純 C 了。
由于 C++ 是解決性能問題的利器,短時間里在市場上沒有真正的競爭對手,對 C++ 的需求會在相當長的時間里一直存在,尤其在大公司和像金融機構一樣對性能渴求的地方。順便提一句,C++ 之父 Bjarne Stroustrup 目前就職的地方便是摩根斯坦利。
### 如何學習 C++?
作為很多聰明人使用過的語言,C++ 在某些場合也可能被用來炫技,寫出除了本人之外誰都看不懂的高抽象代碼。這恰恰是 Bjarne 想努力抵制的方向。他想讓 C++ 對初學者變得更為友好,也明確提出過,他不希望 C++ 是一種讓人們耍機靈的語言,而是一種讓人們更易于使用的語言 [4]。
這同樣也是本專欄的一個目標:我希望你能把 C++ 當作一種實用的語言,能用它寫出抽象但自然的代碼,而非佶屈聱牙、難以卒讀的那種。希望我 30 年的 C++ 經驗能夠給你一點幫助。
學習 C++ 語言就像學一門活躍使用中的外語,你不要期望能夠掌握所有的單詞和語法規則——那對于世界上99.999999% 的人來說是不可能的。但語言是服務于人的,語法規則也是服務于人的,是為了讓人們能夠更好地溝通和表達。雖然 C++ 的每一個新標準都是讓語言從定義和規則的角度變得更復雜,但從用法上來說,新標準允許人們能夠更簡單地表達自己的計算意圖。跟學外語一樣,我們需要的是多看多寫,掌握合適的“語感”,而不是記住
所有的規則。
**Bjarne 有一個洋蔥理論**:抽象層次就像一個洋蔥,是層層嵌套的。如果想用較低的抽象層次表達較高的概念,就好比一次切過了很多層洋蔥,你會把自己的眼淚熏出來的。與這個思路相反,教 C++ 往往有一種不好的傾向,從那些瑣碎易錯的底層教起,自底向上,使得學生常常在尚未領悟到抽象的真諦之前就已經被 C++ 的復雜性嚇翻,從入門到放棄;或者,在學了基本的 C 語法和 class 之后就滿足了,錯過了高級抽象帶來的全新境界。他主張學習應當自頂向下,先學習高層的抽象,再層層剝繭、絲絲入扣地一步步進入下層。如果一次走太深的話,挫折可能就難免了。
作為專欄而非具體的工具參考書,我會重點講是什么和為什么,而不是語法細節。同樣,我也不講或少講技巧,但在需要的地方,我會給出合適的參考資料。希望你在學習了本專欄之后,能夠知道某個 C++ 的功能為什么存在和應該在什么情況下使用。那樣的話,本專欄的目的就達到了。
具體內容上,本專欄共分為四個部分,詳情如下:
* 第一部分——基礎篇,講解現代 C++ 中的最重要特性,幫助你理解基礎概念。
* 第二部分——提高篇,講述幾個獨立的專題,幫助你掌握 C++ 中的一些高級技巧。
* 第三部分——實戰篇,討論實際的工具和第三方庫,幫助你打磨手頭的兵器庫。
* 第四部分——未來篇,討論 C++20 中即將引入的一些新特性,幫助你培養前瞻性。

希望你在完整地學完這四個部分之后,能對現代 C++,這一熟悉而又陌生的語言,有著新的理解,并用它去更好地解決項目中的實際問題。
最后,感謝你的關注。關于 C++ 呢,不知道你有什么樣的看法?你學習它的主要原因是什么?你平時使用 C++ 的場景有哪些?歡迎你在留言區和我交流。
**參考資料**
[1] 游戲葡萄,“《王者榮耀》技術總監復盤回爐歷程:沒跨過這三座大山,就是另一款MOBA 霸占市場了”。http://youxiputao.com/articles/11842
[2] TIOBE Index for November 2019. https://www.tiobe.com/tiobe-index/
[3] Wenzel Jakob et al., pybind11. https://github.com/pybind/pybind11
[4] Interview, “What’s all the C Plus Fuss? Bjarne Stroustrup warns of dangerous future plans for his C++”.
https://www.theregister.co.uk/2018/06/18/bjarne_stroustrup_c_plus_plus/