C++經典學習方法,經驗傳授!

在談如何學習C++之前,我想先簡單聊一下C++是一個什麼樣的語言。

C++無疑是一個公認的比較難上手、難精通的語言,C++賦予了程序員極高的自由度,同時也包括了幾乎所有的編程範式,這使得程序員可以自由地操作計算機的內存,在代碼里嵌入彙編,設計複雜的繼承關係、控制編譯器完成一些運算~~(甚至可以控制編譯狀態來達成某些魔法)~~

這往往要求C++程序員要有紮實的語言基礎,還要有一定的計算機原理相關的知識,否則當我們深入C++時,就會發現C++時時刻刻都在懲罰我們薄弱的基礎和對底層的無知,所以在學習C++的過程中,最重要的就是一定要徹底搞懂學到的語法和其背後的原理。

當然過分深挖語言細節可能會導致初學者在最開始遇到過多的概念,所以在學習初期一個好的檢驗方法是判斷自己對這些概念的理解是否可以自圓其說,即便理解不到位也不要緊,隨着對C++的持續學習,之前不正確的理解必然會被慢慢糾正。

在初學時最好先選定一個小的學習範圍

總體來看,C++包括了四大塊內容:

  • 面向過程的類C語言的部分

  • 以類為核心的面向對象部分

  • 面向編譯器編程的模板部分

  • C++標準庫

初學者往往會對這些豐富的功能感到無所適從,並且可能會遇到一些自己無法解決的bug,C++的學習還沒開始就已放棄……

C++保證了零成本抽象,這意味着沒有使用到的特性不會產生開銷,這也意味着如果你不用那些高級的功能,那麼你就可以當他們不存在。所以劃定一個小的學習範圍開始接觸C++,這是一個比較好的學習策略,可以相對容易的完成學習目標,並且避免使用過多不了解的語法,而導致出現一些複雜的問題。

如果你有C語言的基礎,那麼可以把主要精力放在C++面向對象部分的學習;如果你剛開始接觸程序設計,那麼我覺得先學C語言可能更加合適。C++和C的關係非常密切,並且C++的面向過程部分和C語言基本一致,從C開始,可以幫助你暫時避開C++本身的複雜性來學習C++面向過程的部分。

2寫代碼

編程最重要的就是要實際去寫代碼,不要紙上談兵,我不認為有人可以只通過看書學會任何一門語言。在某種意義上,我覺得寫代碼要比看書重要得多,一個比較高效的學習過程,應該是先通過看書了解基本用法後,立刻去寫代碼。在寫代碼的時候遇到問題,或者想要使用別的用法的時候,再去看書上的相關內容,效果會好很多。如果你只是單純的看了一遍書,那大概看完後你對很多用法依舊很模糊。

另外一點是如果你希望你的C++能力逐步提高,就一定要去主動使用自己不會的用法,切勿一直呆在舒適圈內。在大多數人的印象中C++是一門面向對象的編程語言,面向對象其實是指一種程序設計的方法,也並非C++獨有概念。如果你的C語言基礎比較紮實,你也可以用C語言設計出面向對象的程序,只不過用起來要繁瑣一些。

在學習面向對象部分的知識時,首先要保證的一點是,你確實具備了面向對象的編程思想。想要達到這一目的,你的思考方嚮應該主要放在如何使用面向對象技術來組織具有一定體量的代碼,並且要去體會面向對象帶來的好處,這樣才能夠真正學懂面向對象語言。

因為面向對象並非是C++獨有的功能,目前大部分的高級語言都是支持面向對象的,所以其實面向對象的大部分概念都是相同的。C++提供的面向對象的功能中。比較難理解的應該就是虛函數和多繼承,前者可能需要多花一些時間來徹底搞懂,畢竟這是一個經常會使用到的功能;至於多繼承不了解的話也沒什麼影響,實際很少用到,如果你具有其它面向對象語言的編程經驗,那麼學習此部分應該不算是一件很難的事情。

大多數的C++程序員,對於C++的掌握會止步於面向對象,通常來說掌握這些已經足夠應付日常工作了。

但是如果我們去觀察一下從C++03到C++20之間的功能變化,你就會發現C++一直在完善和強化模板相關的功能,這說明模板是C++的重要組成部分,也恰恰是模板使得C++成為了一門難學難精通的語言。

在學習模板之前,我們需要明確一個極其重要的概念,就是編譯期和運行期,顧名思義編譯期就是指程序編譯的時候,運行期也是同理。我們一般理解的編譯其實就是將我們的代碼轉換為彙編代碼的過程,但C++可以在編譯的時候做更多事情,而這些事情大多就是通過模板來完成的。可以說模板就是C++編譯期和運行期的分割線,在學習C++模板時我們一定要搞明白哪些是在編譯期做的,哪些運行期完成的,這對於模板的學習至關重要。

3學習模板相關知識

接下來我想着重說明一下模板解決了什麼樣的問題。

我們首先考慮在函數接口設計時,需要兼容不同類型數據輸入的情況,而且對於這些數據類型具有相同的處理方法。在這種情況下如果不使用模板,那麼你需要為每一種類型設計專門的重載函數,這就造成了大量的代碼複製,降低了代碼的可維護性。而如果你使用模板就可以一勞永逸的解決這件事情。這樣的用法稱作「模板泛型編程」,主要解決接口兼容性問題。

當然我們也可能會遇到另一種情況,假設你現在需要用一個變量表示一張600*400的RGB圖片數組的長度(你可以100%確定圖片的尺寸無論在現在還是未來都不會改變),那麼你大概率會寫成 `int length = 600*400*3` 而不會寫成 `int length = 760000` 這兩種寫法會產生同樣的用彙編代碼,這也就是說 `600*400*3` 的運算在編譯期已經計算出來了,這可以認為是模板元編程的一個啟蒙思想,只是在元編程中問題不再是簡單的幾個整數相乘,它可能是計算幾個矩陣相乘的結果也可能會帶有判斷邏輯,可以看出元編程主要解決的是在不降低程序運行性能的同時,還提高代碼的靈活性和可維護性。

希望通過上面的例子能夠讓你明白模板的作用,模板相關的概念往往都比較難以理解,所以如果你還不熟悉面向對象部分的知識,那麼最好就當模板不存在,或者簡單的將模板理解為一個特殊的宏。

如果你已經對C++有了一定了解,那麼就可以考慮開始學習模板相關的知識了。學好模板很重要的一點是熟悉C++的類型系統,因為在泛型模板中,更多的是對類型的操作。如果對於類型系統不夠熟悉就可能會寫出很多bug,並且自己也很難找到問題根源在哪。

從個人的感覺來說,通過對模板的學習會讓你更加深刻的理解C++的類型系統。從模板開始你將會接觸到大量的C++語言概念,並且這些概念都是十分重要的,這與面向對象部分的學習有很大的不同。

在模板的學習中,經常出現的那些關鍵概念和關鍵名詞是必須要去搞清楚意思的(這些概念和名詞往往是來自C++標準本身),你需要多去網上查閱資料或仔細研讀書上的例題,並且要多加練習,有必要的話還要設計一些實驗程序來驗證自己的理解。如果你覺得這一部分確實學起來非常困難,那大概是你前期的基礎沒有打好,或者是你確實缺少使用場景,這時你應該暫停模板的學習,去好好鞏固基礎,或者等到有使用場景的時候再來學習模板部分。

4C++的標準庫

最後就是C++的標準庫,標準庫提供了很多好用的工具,可以幫助我們減少「造輪子」的時間。總體來說,標準庫的使用方法學習起來還是比較容易的,在學習時主要關注容器和迭代器還有泛型算法的用法即可。這一部分網上的資料是比較豐富的,無論是用法還是內部實現原理都有詳細的講解,此處就不再展開說明了。

5通過開源代碼學習C++

最後想說一下如何通過開源代碼來學習C++。如果你之前在GitHub上查找過開源項目,不難發現,大部分的項目都大量使用了模板,更是有很多的純模板庫。所以對於C++而言,我不太推薦在沒有模板基礎的情況下去看開源項目,除非你確實找到了一個感興趣並且沒有使用模板的項目(一般這種項目都是比較老的項目,語言標準一般是C++98/03),但這也並不是說只有對模板有較深的理解,才能去看開源項目學習。

實際上你只要知道模板的一些基本語法和特性,就可以去嘗試閱讀開源項目。我比較推薦的是一些比較短的僅頭文件的模板庫(如果你不想看到過多的新特性,最好選語言標準為C++11的庫),通常來說這樣的項目比較適合初學者,讀起來也比較有成就感,通過學習開源項目會極大的提高你對C++的理解,但是選擇適合自己的才是最重要的。