C++之父談關於C++的五個需要被重新認識的觀點(上)(漲知識了)



C++之父談關於C++的五個需要被重新認識的觀點(上)

行業資訊 | 作者:回憶和感動 | 2014-12-18 13:59:54| 閱讀 4568有用 (13) 評論 (2) 收藏


標籤:C/C++
概述:學習和使用過C++的人幾乎都曾經聽說過下面的五個關於C++的觀點,並且對這些話篤信不已,那麼真實的情況是怎麼樣的呢?本文的作者——C++之父Bjarne Stroustrup將會對這些觀點作逐一回擊。

以下的這五個觀點盛行於C++多年:

  1. “要了解C++,你必須先學習C語言。”
  2. “C++是一門面向對象的語言。”
  3. “對於可靠的軟件,垃圾回收機制必不可少。”
  4. “爲了提高效率,你必須編寫底層代碼。”
  5. “C++只對大型複雜的項目有用。”

如果你還對這些觀點深信不已,那麼這篇文章可以給你一些重新認識。這些觀點在特定的時間對於某些人、某些工作來說是正確的。但是對於今天的C++,隨着ISO C++11標準的編譯器和工具的廣泛使用,這些觀點都需要被重新認識。

接下來,我們將會對這些觀點進行逐一反駁。

觀點一:“要了解C++,你必須先學習C語言。”


這不對,事實上對於基礎編程的學習來說C++比C語言容易的多。

C語言雖然幾乎可以認爲是C++的子集,但對初學者來說卻不是最容易學習的。因爲C語言缺乏標記支持和類型安全,並且對於簡化簡單任務來說C++的標準庫更加易於使用。

比如,對於一個非常簡單的用於描述郵件地址格式的函數:

C++之父談C++

它可以被這樣使用:

C++之父談C++

而C語言中需要明確的字符操作和明確的內存管理:

C++之父談C++

然後,它需要被這樣使用:

C++之父談C++

相比之下,哪種版本更加容易學習?哪種語言更加有效率?很顯然是C++了,因爲它不需要計算參數字符,不需要爲簡短的字符串分配動態內存。

對於C++的學習

關於“C語言優先學習”的觀點並非來自少部分人的認識。傳授這種典型觀點的老師主要有以下幾個方面原因:

  • 因爲這是他們在這方面有豐富經驗。
  • 因爲這是課程需求。
  • 因爲這是老師年輕時的學習方式。
  • 因爲C比C++要小,所以更容易學習。
  • 因爲學生遲早都必須學習C語言或者C++的C語言子集。

然而,C語言並不是作爲優先學習的最簡單和有用的C++子集。當你知道足夠多的C++知識後學習C語言則會非常容易。這種學習方式可以有效減輕從C到C++學習時在認識和技術上的困難。

對於現代C++的教學方法,可以參見我的著作:Programming: Principles and Practice Using C++。它甚至在有一章的結尾處展示瞭如何學習使用C語言。這種教學方法在幾所大學的數以萬計學生中使用,非常的成功。它的第二版是使用C++11和 C++14來讓學習變得更加容易。 

C++11標準使C++更容易被初學者接受,例如,這裏是一個元素序列已初始化的vector標準庫:

C++之父談C++

在C++98中,我們只能初始化數組和列表。在C++11中,我們可以定義一個包含有{}和需要的任何類型的初始化列表的構造函數。

我們可以通過for循環的範圍來遍歷vector:

C++之父談C++

對於v的任何一個元素都會調用一次test()。

for循環的範圍可以遍歷任何序列,因此我們可以通過直接使用初始化列表來簡化示例。

C++之父談C++

C++11的目的是使簡單的事情變得簡單。代碼的簡單化並沒有以性能降低爲代價。

觀點二:“C++是一門面向對象的語言。”


不對。C++支持面向對象和其它編程風格,它並不僅限於“面向對象”這個狹隘的觀點。它支持一個綜合的編程技術,包括面向對象和泛型編程。通常一個問題的最佳方式需要比較多種類型。最佳,在這裏指的是時間最短、最易於理解、最有效率和最易於維護等等。

“C++是一門面向對象的語言”的觀點使人們在除非需要擁有許多虛擬(多態運行)函數的巨大類層次結構時纔會考慮使用它。而這種用法對於許多問題來說是不合適的。這個觀點也會導致另外一些人指責C++的面向對象並不純粹。畢竟,如果把“好”和“面向對象”劃上等號的話,C++還包含了其它被認爲是“不好”的非面向對象的東西。這種觀點產生的兩種認識都會導致人們放棄學習C++。(譯者注:作者表達的意思就是把C++比作是一個賣包子和賣米線的餐館。將C++認作是包子鋪會讓人產生2種誤會,其一,路過的人會以爲這裏只賣包子,不賣其它的;其二,愛喫包子的人會認爲包子鋪還賣米線,這包子一定做得不專業)

舉個例子:

C++之父談C++

它面向對象嗎?當然,它嚴重依賴包含虛函數的類層次結構。它是泛型編程嗎?當然是,它嚴重依賴於參數化容器(vector)和泛型函數for_each。它是函數式編程嗎?在一定程度上是,它使用了匿名函數(由[]構造)。那麼它到底是什麼?它是現代C++:C++11。

我同時使用了for循環和標準庫算法for_each只是爲了展示其特性。在實際代碼中,我只會使用其中的一個循環。

泛型編程

你想讓上面那段代碼更通用嗎?因爲畢竟它只適用於vector指針的Shape基類。那麼對於列表和內置數組呢?對於象shared_ptr和unique_ptr這樣的“智能指針”(資源管理指針)呢?對於沒有調用Shape類的對象能夠使用draw()和rotate()麼?可以這樣來做:

C++之父談C++

你可以使用這段程序對任何序列從頭到尾進行遍歷。這是一個C++風格的標準庫算法。我使用了auto來避免必須爲“象Shape類這樣的對象”的接口類型命名。這是C++11的特性,它的含義是“使用被用於初始化的表達式的類型”。所以由for循環中p的類型就能決定這是什麼類型的對象。這種使用auto表示匿名函數參數類型的方法是現已廣泛使用的一個C++14新特性。

如下圖所示:

C++之父談C++

在這裏我假定Blob是包含了操作函數draw()和rotate()的圖形化類型,而Container是容器類型。標準庫list(std::list)擁有成員函數begin()和end(),用於幫助用戶遍歷元素的序列。這是很好很經典的面向對象編程。但是,假如容器不支持C++標準關於遍歷半開序列[b:e)的概念呢?假如庫裏面沒有begin()和end()成員函數呢?或者,由於沒有容器一類的東西因此無法遍歷。對於這些情況,我們可以用適當的語義來定義獨立的begin()和end()。標準庫提供了C語言風格的數組,因此如果容器是C語言風格的數組,問題就迎刃而解了——而C語言風格的數組非常常見。

改寫

來看看一個更難點的例子,假如容器保留了對象的指針,並且有一個用於訪問和遍歷的不同模型呢?比如,你會訪問到象下面的這個容器:

C++之父談C++

這種風格並不少見,我們可以將其映射到[b,e)這樣的一個序列:

C++之父談C++

注意,這種修改是無關緊要的:我並沒有修改容器或者某些由C++標準庫支持的將容器映射到模型進行遍歷的容器類的層次結構。這是改寫的一種形式而不是重構。

我選擇這個例子是爲了說明這些泛型編程技術並不侷限於流行的標準庫。它們也符合常見的“面向對象”的定義,但是它們卻不是面向對象的。

關於C++的代碼一定是面向對象(意味着在每個地方都會使用層次結構和虛函數)的觀點深深地影響了人們對C++性能的評價。還有一些人認爲當需要解決多種類型的運行的問題只有面向對象纔是最好的。在以前,我也是這麼想的。但是事實上,它也有死板的一面(比如並不是所有相關類型都屬於同一層次結構)並且虛函數無法作爲內聯函數(這就使得處理許多簡單而重要的任務時會多花費大量的時間)。

下一篇將會圍繞“對於可靠的軟件,垃圾回收機制必不可少。”的觀點進行說明……

本文翻譯自Five Popular Myths about C++, Part 1,作者爲:C++之父Bjarne Stroustrup 

本文譯者爲慧都控件網——回憶和感動,轉載請註明:本文轉載自慧都控件網

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章