C++的沉迷與愛戀

每年的 09/28 於我都是一個特殊的日子 -- 不只是因為教師節。今年很特殊地沒有普天同慶,那麼我就寫篇文章自己慶祝一下好了。

我於今年七月發表了一本著作《多型與虛擬》和一本譯作《深度探索C++物件模型》,獲得很大的回響。這些作品都不是針對 C++ 的完全初學者所寫,但從初階到高階為數眾多的 C++ guy,熱情地表達了他們對這些主題的喜悅。
在許多來信中,我看到一些有趣的現象,也感受到一些值得整理下來的想法。所以,根據我個人的學習過往、我的教學經驗、以及周遭朋友的心得交流,寫下這篇文章,或可為後學者戒。

●《多型與虛擬》序言節錄
首先讓我節錄《多型與虛擬》一書序言:
《多型與虛擬》序 節錄(侯俊杰/松崗/1998/07)
一般而言,C++是一個難學易用的語言。
C++的難學,初始在於其重重的布幕,布幕之中編譯器對我們的程式碼做了太多的手腳,使我們慣於循序思考的工程腦袋一無所措。及長,又面臨新的思維模式,使我們必須扭轉慣常的思考習慣。
C++的易用則在於其巨大的彈性,能夠以多型(polymorphism)、虛擬(virtual)、模板(template)、泛型(generalization)等種種型式,讓既有的碼去處理未知的、未來的資料型態。
當然,易用必須先能用。用不好或不能用的話,「寫 C++ 程式」最後就成了只是「使用 C++ 編譯器」,這是大家常拿來彼此調侃的笑話。
在「難學」的背景下,「易用」是使我們依然前仆後繼的動力。愈來愈多的大學資訊科系把 C++ 開在大一課程,這雖然說明 C++ 是多麼地重要,可也苦了資訊新兵們。
其實「難學」的最大癥結在於,很難得有一本書,能夠一針見血地指出多型與虛擬的重要性;在我們粗具語法基礎之後,直接把我們導引到最核心最重要的思想,并且在建立這個思想的過程中,提供足夠的必要基礎。
●困難度之一
「C++ 是個難學易用的語言」,這句話相信很多人心有戚戚。C++ 的學習難度,一在於語言本身太多的「幕」,一在於 "paradigm shift" (思考模式的移轉)。
傳統循序語言如C,Pascal,Basic,Fortran...,除了模樣看起來稍有不同,基本上都是函式call來call去,大同小異,很容易掌握。你想做的動作,在code中都看得一清二楚。你所看不到的,犖犖大者也不過就是編譯器為你的函式加上用以處理堆疊的一小段碼(prologue和epilogue),這一小段碼基本上做的是housekeeping工作,你沒看到也沒有關系(更好),并不影響你對程式邏輯的思考。
C++不一樣,C++有太多和程式邏輯息息相關的動作是編譯器為我們加上去的。換句話說C++編譯器為我們「加碼」。如果不識清這一節,學習C++有如霧里看花,霧非霧,花非花。
編譯器為我們的C++程式加了什麼碼呢?很多!物件誕生時ctor會被喚起,物件死亡時dtor會被喚起,這都是加碼的結果。ctor中設定vtpr和vtbl,這也是加碼的結果。new 單一物件時會產生 memory block cookie,new物件陣列時會產生一個內部結構記錄著 object size和 class ctor...,這也都是布幕後的工作。可以說,程式碼中看不到而卻必須完成的所有與程式邏輯有關的動作,統統都是C++編譯器加碼後的結果。
當「繼承」發生,整個情況變得稍微復雜起來。「多重繼承」又更復雜一些,「虛擬繼承」再更復雜一些。
這些布幕後的主題,統可歸類為所謂的C++ object model(物件模型)。如果不知道這些底層機制,你就只能夠把"make destructors virtual in base classes"(《Effective C++》, item14)或 "never treat arrays polymorphically" (《More Effective C+》, item 3)這類規則硬背下來,卻不明白它的道理。
用一樣東西,卻不明白它的道理,林語堂如是說:『不高明』。只知道 how,不知道 why,侯捷如是說:『不高明』。
●困難度之二
C++ 的第二個學習難度在於 "paradigm shift"(思考模式的移轉)。別說自己設計classes了,光使用別人的classes,就都是一種思考模式和行為模式的移轉。MFC(或 OWL 或 VCL)programmer 必然甚能夠領略并體會我的意思。
使用所謂的application framework(一種大型的、凝聚性強的、有著物件導向公共基礎建設的 class library),你的碼和framework之間究竟是怎樣的關系呢?framework提供的一大堆可改寫的虛擬函式的意義與價值究竟在哪里呢?為什麼framework所設計的種種美好性質以及各式各樣的演算法竟然可以施行於我們自己設計的class types身上呢?framework被設計時,并不知道我們的存在呀!
這正是物件導向中的多型(polymorphism)的威力。
稍早所說的C++物件模型,偏屬程式設計的低層面;這里所說的思考模式移轉,則是程式設計的高層面。能夠把新思維模式的威力發揮得最淋漓盡致的,當推物件導向的polymorphism(多型)和 generalization(泛型)。如果你沒有使用這兩項特性,等於入 C++ 寶山而空手返。
●反覆煉,循環震蕩
想像 C++ 是一把用來解決程式問題的刀,要它堅軔,要它鋒利,就必須經過多次的回火,在高熱和驟冷之間煉。
初學 C++ 語法(syntax)之後,你應該盡快嘗試體驗 polymorphism (大致而言也就是虛擬函式的運用)。等到對 OOP 的精神有了大局掌控的能力,但對 C++ 的許多小細節不甚清楚,就是回到C++ 物件模型煉的時機。
成長,是在高階(polymorphism)和低階(object model)之間反覆震蕩,才能夠震蕩到更高的位階,而不是平平庸庸於中階(C++ syntax)的一灘死水。
●不要沉淪於 C++ syntax
100個人跟我說他懂 C++/OOP,只有10% 不到可以讓我認為他沒有胡吹大氣。太多的人,上嘛上不到 polymorphism,下嘛又下不到object model。就這樣不上不下地卡在 C++ 語法層面。大一學了C++,到大四快畢業了,連 virtual functions 是怎麼回事都期期艾艾支支吾吾說不出個道理。
有時候我覺得,太苛責同學也於心不忍,因為同學們事實上處於一種無知的狀態,既不知道 C++/OOP 該怎麼學,也不知道哪些書可以教他們那麼學。所以,苛責同學,不如責怪老師。
眾所周知,大學教授泰半是動口不動手,普遍的心態是「論文第一,升等為要;程式語言?哎,末流!」。「末流」課程通常由教授們輪流教,誰倒楣誰來教;於是就 常常有「下學期要教 C++ 語言了,這學期寒(暑)假趕快去要本書來惡補」的情況發生。偏偏程式語言這東西,只動口不管用,一定要動手,而且要常動手。老師自己沒有摸到C++/OOP的精神,學生又能學到什麼?
有些學校資訊系并不教特定的程式語言,老師們的態度是「語言是一種自己學就好了的東西嘛,拿到大學殿堂來,哎,不入流」!於是應該好好為學生打下實際基礎的課程,卻天馬行空地騰云駕霧起來,大談抽象意念。飽讀經書的老師們可能忽略了,一個完全沒有技術 基礎的學子,要的不是形而上的道,而是形而下的器。
我們是先能夠欣賞具象畫,還是先能夠欣賞抽象畫?我們不都是先對畢卡索的畫大罵「這是什麼東西」,直到自己的藝術涵養夠豐富了、人生閱練更飽滿了、能夠舉一隅以三隅反了、能夠接觸類旁通左右逢源了,才轉而能夠欣賞甚至進入畢卡索的抽象意境嗎?
老師們各有專長,要老師們來教非彼專長的大班課、基礎課,我又覺得似乎也太為難老師了。那麼,苛責老師,不如責怪學校當局。如果學校當局能夠聘請經驗老道又有教學熱誠的工程師來教這類實務學科,不是三方皆大歡喜嗎?不要說什麼制度僵化啦,難以突破啦,大學是高度自治區,禮聘幾位兼任老師,不全都是系上的權責范圍內嗎?
當學子們在課程上學不到他要的東西,就只好閉門自修。但是,循序性(sequential)語言尚有自修學會的可能,物件導向語言嘛,以大學生的程度來講,我認為自修實在困難,只會修出個四不像、半瓶水。
管不到學校!管不到教授!自求多福的情況下,希望看到這篇文章的你,知道 C++/OOP 該怎麼學。
●不要沉迷於 C++ semantics 和 C++ object model
對 於底層知識有濃厚興趣的朋友,下探到 object model 領域,一定會非常開心地在 object size、object layout、vptr/vtbl、以及許多布幕後的技術之間玩將起來。了解這些東西,當然是好的,但是由於一探究竟得其奧秘的快感與成就感,使得一些朋 友們在這個層面里「玩」起來了,小地方玩得很精,玩得不亦樂乎,玩得忽略了 C++/OOP 的最終目標。
最終目標是 polymorphism!
我要說,在 C++ syntax 以及相對低階的 C++ semantics 里,不要玩得太過火。過猶不及,會傷身的。C++ 經典名書內附的一些習題,在我看來頗有點玩得過火的味道。至於什麼百題精選、題庫大成,除了修練基本功之外,都滿無趣的東西。
Programming 應該是一種天馬行空的想像力與創意的組合;如果你能夠自己想題目,譬如說實作一個天體運行的 class 體系、或是實作一個生物分類(界門綱目科屬種)體系,不是很有趣嗎?準備資料的過程中,查查百科全書,你也因此查到了太陽系九大行星的幾何資料,哈雷慧星 的軌道周期,或是黑面琵鷺的「界門綱目科屬種」英文名稱,這難道不比鉆研於 ++++i 或 ----i 或 *&*&p 之類的頭腦體操題目有趣嗎?(看過不少這類好笑題目,沒一個記下來,只好胡亂寫幾個運算式。諸位應該知道我說的那種頭腦體操題目)
固然,在科學與工程的領域里頭,無技術無以為立,但別把自己弄得過於僵化,過於匠氣。僵化與匠氣是我們教育體系的最大沉疴。到了高專層次,敗象顯露無遺。
●名書推薦
如果沒有介紹幾本好書,我就是為德不卒。
讓我再節錄 《多型與虛擬》 的二刷感言:
《多型與虛擬》一版二刷感言(侯俊杰/松崗/1998/08)... C++ 相關書籍,如天上繁星,如過江之鯽。廣博如四庫全書者有之(如 The C++ Programming Language、C++ Primer),深奧宛如山重水復有之(如 Inside The C++ Object Model),獨沽一味者有之(如 C++ Programming Style、More Effective C++),獨樹一幟者有之(如 The Design and Evolution of C++),另辟蹊徑者亦有之(如 STL tutorial Reference Guide)。...?
以下是我認為你應該要擁有的書籍。有趣的是,我才在自己班上做了一個調查 (我教的是物件導向 Windows 程式設計,學生應該要有良好的 C++/OO 基礎),擁有以下 1~5 本書的人舉手。舉手人數都很少,而且老是那幾位(最高記錄是擁有四本)。這讓我感覺,強者恒強,弱者恒弱。悲夫!
1. C++ Primer (3/e), Lippman/A.W./1998?
(聽說 1999 將有中譯本)
2. The C++ Programming Language (3/e), Bjarne/A.W./1997?
(聽說 1999 將有中譯本)
以上兩本書是 C++ 經典百科。就內容水平而言,我認為同為瑜亮。 普遍的印象是,第一本較易接受,第二本澀味稍重。第二本書 作者 Bjarne 是 C++ 語言的創造者,所以有其權威性。我認識的多 位 C++/OOP 高手,都是兩書齊具。
3. Inside The C++ Object Model, Lippman/A.W./1996?
(中譯本 <深度探索 C++ 物件模型>)
全書講解 C++ object model,上窮碧落下黃泉。內容很好,層次也高, 可惜原文書大大小小的錯誤繁如晨星,閱讀時需小心。
4. Effective C++, Meyers/A.W./1992?
(印象似有中譯本,名稱忘了,誰可補充說明?)
5. More Effective C++, Meyers/A.W./1996?
(有中譯本嗎?我不知道,誰可補充說明?)
同一作者的這兩本書,專講 C++ programming 的重要觀念,使你的程式 更穩健更有效率。書中許多觀念涉及 C++ object model,與 (3) 書混合看, 如魚得水。
6. Polymorphism in C++ <多型與虛擬> 侯俊杰/松崗/1998?
(沒有中譯本 -- 它本身就是中文書)
在語法粗具的基礎上,直接把讀者導引到最核心最重要的思想,并且 在建立這個思想的過程中,提供足夠的必要基礎。

我只列出一本中文書,是因為這方面的中文書我看得少,英文書看得多。「恐有遺珠之憾」這類「八方得體」的話,還是說一下好了 :)。
注意,這些都只是強本固元用來扎基礎的書籍而已,要觀摩大型程式經驗,還有諸如 Large Scale C++ Software Design(John Lakos/A.W./1996)可以閱讀。

OO 的世界,不止 OOP,還有 OOA/OOD,那又是一缸子的學問和一缸子的書。
參考:http://blog.csdn.net/kenlinyh/article/details/458044
分類:?C/C++編程

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383990.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383990.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383990.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Insertion Sort——打表找規律

【題目描述】 Insertion sort is a simple sorting algorithm that builds the final sorted array one item at an iteration.More precisely, insertion sort iterates, consuming one input element each repetition, and growing a sorted output list. At each iteration…

數據鏈路層: 可靠性傳輸 六個協議

可靠性傳輸 1. 差錯控制 發送方將數據幀發送, 但是當發送方發送的是一個 1的時候此時接受方卻接受的是一個 0. (1)校驗 接收方如果幀校驗接受到的幀沒有問題, 則對發送方發送一個肯定性的確認, 當對這個數據幀進行校驗發現這個幀有問題的時候, 此時接受方一種是將這個數據幀…

c語言實現配置文件的讀寫

配置文件的格式如下&#xff1a; key1 value1 key2 value2 . . . 名值對以一個鏈接&#xff0c;一條記錄以換行符分割 頭文件&#xff1a; #include<stdio.h> #include<stdlib.h> #include <string.h> 函數原型&#xff1a; void trim(char *strIn, char *…

Educational Codeforces Round 73 (Rated for Div. 2)

A 很簡單的一個模擬&#xff0c;只要前面的數字有兩個以上就能合成后面的&#xff0c;我們進行一遍合成看能不能出現2048就可以了。 #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<iostream> #include&…

數據鏈路層: HDLC

一. 協議機 發送方和接收方. 同時有限狀態機把協議形式化為一個四元組 (S,M,I,T), 其中你S表示進程和信道可能進入的集合, M 表示數據幀的狀態, I 表示進程的初始狀態, T 表示兩兩狀態之間的轉化. 每個系統狀態可以分為發送狀態, 接受狀態和信道狀態. 把狀態用一個點進行表示,…

Miller_Rabin算法

為了測試一個大整數是不是素數&#xff0c;我們不能夠使用傳統的測試是否有因子的方法&#xff0c;因為那樣的時間復雜度至少也是O(n)O(n)O(n)&#xff0c;空間復雜度是O(n)O(n)O(n)&#xff08;使用線性篩數法&#xff09;&#xff0c;時間復雜度還好說&#xff0c;空間復雜度…

bob-tong 字符串函數之Strtok()函數

https://www.cnblogs.com/Bob-tong/p/6610806.html Strtok()函數詳解&#xff1a; 該函數包含在"string.h"頭文件中 函數原型&#xff1a; char* strtok (char* str,constchar* delimiters ); 函數功能&#xff1a; ??切割字符串&#xff0c;將str切分成一個個子…

數據鏈路層:SLIP(串型線路IP) PPP(點對點協議)

SLIP 沒有差錯控制, 傳輸時必須知道對方IP, 傳輸使用于低速業務 19.2k.應用非常受限 PPP協議 1. PPP協議功能 處理錯誤檢測 支持多協議(IP, IPX, DECnet 等) 連接時允許協商 IP 地址 允許身份驗證 2. PPP 的組成 串型鏈路上封裝數據報, 即支持異步鏈路也支持面向 比特…

Honeycomb——BFS

【題目描述】 傳送門 【題目分析】 看起來很復雜好像還要建圖什么的&#xff0c;其實直接在原圖上BFS就可以了&#xff0c;設置一下方向數組&#xff0c;然后直接跑就可以了。 【AC代碼】 #include<cstdio> #include<cstring> #include<algorithm> #inc…

C語言中strspn()函數和strcspn()函數的對比使用

C語言strspn()函數&#xff1a;計算字符串str中連續有幾個字符都屬于字符串accept 頭文件&#xff1a;#include <string.h> strspn() 函數用來計算字符串 str 中連續有幾個字符都屬于字符串 accept&#xff0c;其原型為&#xff1a; size_t strspn(const char *str, con…

Codeforces Round #587 (Div. 3)

A 只要每兩個都不一樣就可以&#xff0c;一旦出現兩個一樣的就改一個。 #include<cstdio> #include<cstring> #include<algorithm> #include<climits> #include<cctype> #include<queue> #include<set>using namespace std;typede…

信道分配 以太網

1.頻分復用 將信道分為多個頻帶, 用戶得到某個頻帶后,在通信的過程中, 自始至終都都占用這個信道.即頻分復用中, 所有用戶同時占用不同頻帶的信道 2. 時分信道 將時間劃分為一段一段的等長時分復用幀, 每個用戶在不同時間占用相同的數據幀 3. CSMA/CD 載波監聽多點接入/碰撞…

strpbrk函數

http://blog.csdn.net/tommy_wxie/article/details/7554332 函數原型&#xff1a;extern char *strpbrk(char *str1, char *str2) 參數說明&#xff1a;str1待比較的字符串&#xff0c;str2為指定被搜索的字符串。 所在庫名&#xff1a;#include <string.h> …

網絡層網絡層服務及其 IP 地址

ARP 協議功能 將 IP 地址通過廣播(一個網段, 不能跨路由器), 目標 MAC 地址是FFFFFFFF 解析目標IP地址的 MAC 地址. IP 協議 網絡層的一個協議, 是一個協議的統稱, 包括 ARP(地址解析協議) 協議, ICMP(網絡控制報文協議) 協議, IGMP(網際組管理協議) 協議. 其中 ICMP 和 IG…

隨機生成1024個數,存入一段內存,用指針實現獲取1024個數的最大數地址,最小數地址

http://blog.csdn.net/itcastcpp//details/39277193 題目&#xff1a;隨機生成1024個數&#xff0c;存入一段內存&#xff0c;用指針實現獲取1024個數的最大數地址&#xff0c;最小數地址&#xff0c;具體實現如下&#xff1a; [cpp] view plaincopy #include<stdlib.h> …

UVa11134

【題目分析】 覺得是一道挺考驗貪心掌握程度的題目&#xff0c;我就算知道是要用貪心而且肯定和區間有關&#xff0c;肯定要進行一下排序什么的我還是沒有找到合適的貪心策略。 經過大佬的博客后我才明白如何進行貪心。 如果沒有任何提示看這道題&#xff0c;首先&#xff0…

傳輸層:IP 地址解析 路由轉發

IP 地址與硬件地址 1. 地址解析 通過IP地址將其如何轉換為 MAC 地址.解決同一個局域網上的主機或路由的 IP 地址和硬件地址的映射問題. 即以太網上除了主機還有路由. 即如果發出的請求所有的主機都沒有做出相應, 那么該以太網上的路由會對其做出響應. (1) 以太網內部主機與…

UVa11582

一個數學問題,一旦出現循環確定循環節以后就能解決問題啦. 加上一個快速冪取模.需要注意的是數據范圍是264,所以必須用unsigned long long才能解決問題. 覺得板子還是要會自己寫,否則不同的題目具體有一些小的改變就會束手無策. 還有就是發現如果每次初始化數組的話就會超時,所…

輸入一個單向鏈表,輸出該鏈表中倒數第K個結點

http://blog.csdn.net/itcastcpp/article/details/39274891//尹成 單鏈表操作 #include <iostream> using namespace std; class LinkList;struct LinkNode { public:LinkNode(int value 0):m_value(value),pNext(NULL){}~LinkNode(){pNext NULL;}friend class LinkList…

網絡層:構成超網(CIDR)

CIDR構成超網 CIDR消除了原來的傳統的 A,B, C, D類地址, 使用了各種網絡前綴來代替原來分類地址中的網絡號和子網號, IP 地址由原來的三級分類又變成了兩級分類. 其中網絡號和子網號是一個隨機的長度. 其中 CIDR 也可以使用 / 的形式來表示, 其中在 / 前面寫上網絡前綴的位數.…