C++ 之父 Bjarne Stroustrup:簡單的表述方式纔是最優的方案

本文僅用於學習和交流目的,不得用於商業目的。非商業轉載請註明做譯者、出處,並保留本文的原始連接:http://www.ituring.com.cn/art...html

訪談嘉賓

Bjarne Stroustrup(本賈尼·斯特勞斯特盧普)git

圖片描述

1982年,貝爾實驗室(美國AT&T公司)的Bjarne Stroustrup博士在c語言的基礎上引入並擴充了面向對象的概念,發明了新的程序語言C++。之因此被命名爲C++,是爲了表達該語言與c語言的淵源關係。Bjarne Stroustrup博士所以被尊稱爲「C++語言之父」。程序員

以後,面向對象的編程思想開始席捲整個開發領域,標準模板庫(STL)和微軟的VC++平臺推波助瀾,C++開始流行起來。能夠說,C++對整個軟件開發及IT業的貢獻,不言而喻。github

C++仍在它擅長的領域發揮着不可或缺的做用。做爲C++之父,Bjarne Stroustrup也一直致力於C++標準的改進和推廣,其著做《C++編程語言》《C++的設計和演化》和《C++加註參考手冊》等已成爲C++學習的經典讀物。算法

訪談內容

英文版:http://www.ituring.com.cn/article/273874編程

除了做爲編程技術大師爲人熟知之外,Bjarne還有不少至理名言被你們普遍引用。在觀看以前的訪談時,我也被您發人深思和辯證的思惟所折服。如何作到將天然語言和編程語言運用如此得體的高度呢?數組

我想要直接而簡潔地表達觀點,雖然並不總能成功,但這值得一試。記住,當你寫代碼的時候,並不只僅是給編譯器看的。相反,代碼的「消費者」包括全部閱讀和維護代碼的人。若是你的代碼醜陋不堪、難以理解,它將沒法運行甚至形成巨大的維護問題。所以,不管是代碼仍是「普通文本」,其目的都是清晰地表達觀點,幫助其餘人理解這些想法。寫做是一種縷清思路的方法——對本身和他人來說,都是。緩存

我記得,您曾經討論過人們對C++的誤解(要理解C++,首先要學習C語言;C++是一種面向對象的語言;可靠的軟件須要垃圾回收機制;爲了提升效率,必須編寫低級代碼;C++只適合大型複雜的程序)如今,他們的偏見有所改觀嗎?安全

一些人瞭解了,還有不少人沒有。這些謬見廣泛充斥於網絡上、文章和教科書裏,一般被理所固然地接受——即使沒有證據支撐仍然被看成事實陳述。因此,很難進行反駁。相信這些謬見的人並不認爲本身對C++持有偏見,他們認爲本身是進步的,甚至由於這些觀點變得優越。微信

我想借此機會,鼓勵你們花點兒時間(從新)審視下本身的觀點,同時簡單陳述下我本身的一些觀點。若是想從技術角度瞭解詳細的論證過程,請參考個人相關論文和書籍。

要理解C++,必須首先學習C語言。不是的,若是你自己已是一名程序員了,徹底能夠直接進入類設計和使用各類庫。若是你剛開始接觸某種語言,可以處理低級的編程問題,能夠依賴C++的強類型檢查和各類庫更容易、更快速地掌握基礎知識。編程新手在使用低級工具(指針、數組、malloc()或free()、casts、宏)的時候,沒有理由立刻就瞭解它們存在的問題。複製或比較C語言裏的字符串對於編程新手來講是痛苦、單調乏味的。

固然了,不瞭解指針、數組、自由存儲管理(動態內存管理、堆)等方面的知識,就不能在C++上有所建樹,但能夠以後再學習,等掌握了編程常識和C++基礎之後再學習。基於這樣的想法,我爲大學新生(一年級學生)設計了一套課程並編寫了相應的課本:http://www.stroustrup.com/pro... 。效果很好。

C++是一種面向對象的語言。不是的,對於大多數包含繼承性的傳統意義上的OO來講,不是這樣的。C++確實支持面相對象編程技術,也至關優秀,但這並非C++的所有。現代C++,包括大部分的ISO C++標準庫,更多地再也不遵循這種模式。C++開始使用簡單的具體類型和獨立函數,而且僅在應用程序域採用分層架構、須要運行時調用的時候才使用運行時多態性。大多數受歡迎的C++應用程序使用了不少技術,並不只僅是傳統的面向對象技術,有時候甚至根本沒有采用面向對象技術。

可靠的軟件須要垃圾回收機制。不是的,GC有時會阻礙可靠性的達成。GC並不能消除全部的內存泄漏,不能解決非內存資源的管理問題。泄漏套接口、文件句柄、線程和鎖,可能比內存泄漏更容易讓系統中止。支持可靠性的最好辦法是,找到應對資源管理和錯誤處理的方法,好比C++提供的RAII (Resource Aquisition Is Initialization, 資源獲取即初始化)。我目前正在研究這種方法,爲資源安全和類型安全的C++提供一套全面的系統:Http://www.stroustrup.com/res... 。核心觀點是保證沒有泄漏,使垃圾收集器沒有必要存在。在不影響程序員用代碼簡單、直接地表達想法的前提下,保證沒有泄漏確實很難,但不是沒有可能。

爲了提升效率,必須編寫低級代碼。不是的,現代C++十分擅長低級優化和不一樣抽象層次間的優化,多少數量的代碼都沒法跟這種能力相比,特別是現代架構具備深度緩存層次結構和配有大幅度指令調序的優化器的狀況下。從更高的層面說,人類沒法經過直接使用線程和鎖,獲得最優化的結果,因此咱們須要更高級的模型和算法,得到正確性、可靠性、可預測性和原始性能。當關於機器、數據或算法的一些觀點被證實是毫無根據的時候,擺弄bit、byte和指針這些基礎會變得可悲。舉個列子,看一下我和別人合著的這篇文章(http://www.stroustrup.com/imp... .)。文章經過去掉精心設計的優化,提升了spec-mark程序的性能。最後生成的程序變得更精簡、更清潔、易於維護、可擴展,並且沒有反作用。關於零開銷抽象(zero-headed abstraction)的問題,我已經談了不少。最近,我還看到了不少負開銷抽象(negative-headed abstraction)的示例:經過簡化適當的抽象得到最優化程序。

C++只適合大型複雜的程序。不是的,除非你認爲一兩頁代碼的量就算是大型、複雜的項目。要知道,任何有影響的程序都須要用到一個或更多個庫。這適用於每一種語言。不用任何庫,單憑光禿禿的語言,對於編程人員來說是痛苦的也是徒勞的。

在討論C++或是(更糟地)下定結論時,隨隨便便地堅持某種謬見,不加思考,只能說明懶惰。以前,我也寫了一篇澄清這些謬見的文章:www.stroustrup.com/Myths-final.pdf 。

C++並不是靜止不前的。標準委員會很快就將宣佈C++17的新增特徵。您認爲哪些特徵是值得期待的?

C++17新增了不少小的改進,對於每個程序員來講都值得期待,但不要期望特別重大或是顛覆性的改進出現。預計在2017標準發佈後,這些新增特徵隨便可以在全部主要的編譯器裏應用。事實上,大多數C++17的特徵已經能用了。

新增特徵不必定對全部人有幫助,大可能是爲了特定羣體的須要而完善C++或是標準庫的。你能夠在搜索引擎裏輸入C++找到新增特徵的詳細列表,不過,我會在這裏簡要談幾點我所喜歡的特徵:

  • 結構化綁定:使用C++17,咱們能夠打破結構,爲結構成員命名。例如:

map<int,string>mymap;  
 //...  
 auto[iter,success]=mymap.insert(value);  
 if (success)f(*iter);

對於map<int,string>,insert()返回pair<mymap<int,string>::iterator,bool>,如今咱們能夠命名兩個返回值並直接使用,而不用建立一個pair對象,再訪問它的成員。

對於循環控制,這一點特別有用:

for(const auto&[key,value]:mymap)  
      cout<<key<<」->」<<value<<’\n’;
  • 咱們用std::variant 讓union的顯式使用變得冗餘。如今,咱們能夠

variant<int,double>v;       //能夠是int或是double  
 v=12;  
 auto i=get<int>(v);         //i 變成了12  
 auto d=get<double>(v);      //會拋出bad_variant_access異常
  • 對於以前沒有定義求值順序的狀況,如今,多數狀況下是能夠定義的。例如

count<<f(x)<<」」<<g(y)<<’\n’;

能夠保證輸出g(y)的值以前先輸出f(x)的值。在C++17以前,f(x)和g(y)是能夠交錯的,這容易產生bug和混亂。

到2020年,咱們將看到進行了重大改進的C++20。例如,

  • 概念——顯著簡化、更好指定的泛型編程

  • 模塊——更好的模塊化、更快的編譯

  • 協同程序——更加簡單、快速的生成器和pipelines

  • 庫——簡單、更快、更靈活的網絡

  • 新版STL——更快、更簡單、更靈活的算法和ranges

這並非科幻小說裏的幻想,不少特徵已經開始在某些領域應用了。問題是,ISO C++標準委員會可否經過。

是否能夠用某個新增的特徵爲例,向咱們展現一下該特徵是如何符合C++的演化原則(直接硬件訪問;零開銷抽象;靜態類型)的?

提升硬件訪問能力和低級代碼的性能,是一項須要付出長期努力的任務。有些努力是看得見的,有些不容易看獲得。

咱們一直在努力提升編譯時的計算能力,constexpr是這方面的典範。使用constexpr,咱們能夠指定一個函數在編譯時取值,若是用常量表達式做爲參數的話。一樣,咱們也能夠確保編譯時就完成某項計算工做。

constexpr int isqrt(int n)     //編譯時取值爲常量參數  
 {  
     int i=1;  
     while(i*i<n) ++i;  
     return i-(i*i!=n);  
 }  
 constexpr int s1=isqrt(9);    //s1是3  
 int x;                        //not a constant  
 //…  
 constexpr int s2=isqrt(x);    //編譯時,出錯  
 count<<weekday{jun/21/2016}<<’\n’; //星期二  
 static_assert(weekday{jun/21/2016}==tue);

Constexpr配合使用const能夠有效地提升性能,減小代碼大小,並提高代碼、ROM中數據處理的能力。由於「你不能對常量創造某個競爭狀態」,因此有助於併發系統。

另外一個不太明顯的例子是,C++17確保多數狀況下的複製省略。它讓咱們能夠從函數中方便地獲得值。例如

T compute(S a)
 {
     return complicated_computation_yielding_a_T(a);
 }
 T t=compute(s);

這裏沒有副本!能讓咱們從指針和動態內存中解脫出來,由於在現代的硬件訪問中,間接和動態內存愈來愈昂貴(相對地)。若是和以前講到的結構化綁定結合使用,會更有趣

pair<T,T2>compute(S a,S2 b)
 {
     return{ comp1(a,b),comp2(a,b) };
 }

 auto[foo,bar]=compute(s,s2);

一樣,這裏不須要複製。

在過去的二十年裏,模板一直被認爲是零開銷抽象的,獲得了迅猛的發展。它被普遍複製於其它的編程語言中,但一般並不靈活,也不如C++模板運行時的效率。可是,模板基本上會提供編譯時的duck typing,而不是基於檢查接口的程序;它們會在以後的實例化階段進行類型檢查。所以,模板的迅猛發展致使了至關複雜的編程技術問題。咱們須要讓泛型代碼更接近於非泛型代碼,更容易編寫,更易於編譯器檢查同時不影響或限制表達性。

Contexpr 函數的功能包括:再也不須要模板就能夠獲得編譯時計算的值。若是你只須要某個類型的值,函數就能很好地表達。使用contexpr,編譯時函數就能像其它函數同樣,類型檢查也能像其它函數的同樣(不一樣於宏技巧或是傳統模板的元編程)。

「概念」是支持模板接口規範的語言特徵。遺憾的是,它沒能成爲C++17的新增特徵,但做爲ISO 技術規範已經應用於GCC6.2了。概念能夠解決模板的不少問題。考慮一下advance(),簡化版的標準程序庫函數,它容許迭代器向前移動n個元素。假如咱們須要兩個版本,一個用於列表之類的東西,每次移動一個元素、操做n次;一個能夠直接移動n個元素:

template<Input_iterator Iter>
 void advance(Iter p,int n){while (—n)++p;}

 template<Random_access_iterator Iter>
 void advance(Iter p,int n){p+=n;}

也就是說,若是參數是一個隨機訪問的迭代器,使用第二種快速的版本;不然,使用第一個慢版本。

void(vector<int>::iterator pv, list<string>::iterator pl)
 {
     advance(pv,17);          //fast
     advance(pl,17);          //slow
 }

這是優化後的快速方案,我用短短几分鐘就能向新手解釋清楚。它跟「傳統模板編程」不一樣,在編寫方式和檢查方式上都不一樣。若是願意,我甚至能夠進一步簡化adavance的定義:

void advance(Input_iterator p, int n){while(n--)++p;}
 void advance(Random_access_iterator p, int n){p+=n;}

這徹底符合咱們談論代碼的方式,任何一個天真的程序員也會至關合理地這樣認爲。

最近,我寫了一篇文章(www.stroustrup.com/good_concepts.pdf)詳細解釋了concepts的觀點。

某種程度上講,C++對專家更友好,只有少數的專業人士才能很好地掌握C++。如何減小初學者的困難呢?

「只有少數的專業人士可以很好地掌握C++」誇大了C++的難度,由於確實有數以百萬的程序員們用C++編寫出了優秀的系統。但坦白說,不少C++代碼並不符合專業質量的要求,咱們還能作得更好。

C++讓編程專家很容易編寫出複雜、高性能、低資源消耗的代碼,但不足以成爲廣大普通程序員喜好的語言,它須要簡化。

我努力說服ISO C++標準委員會的專家還有許多的編程教師,說明咱們須要不斷的努力,開發和講授更簡單的方式,不能僅僅專一於最優化和最聰明的技巧。一般狀況下,簡單的表述方式纔是最優化的方案,「聰明」的技巧對於讀者、維護人員、優化器來講多是一種負擔。在談論用代碼表達思想的時候,我大多會用「聰明」表示「太複雜」。最好把聰明用在分析問題和找尋根本辦法上。

「用簡單的方案解決簡單的事情。」以前,C++98 標準模板庫採用for語句來控制循環執行:

for(vector<int>::iterator p=v.begin();v!=v.end();++p)
     cout<<*p<<’\n’;

在C++11中,咱們使用range-for-statement :

for(auto x:v)
     cout<<x<<’\n’;

意思是「輸出 v 中的全部成員 x」。auto表示「讓 x 具備初始化器的類型,在這裏,也就是 v 的元素類型」。

語言特徵和標準庫中的組件並不能很好地處理複雜性問題。因此,我開始制定一些指導準則,幫助你們更好地使用C++。個人這一作法也獲得了其餘人的支持,目前咱們正在一塊兒開發名爲「C++核心準則」的項目,以及工具支持的相關問題。你能夠到麻省理工開源項目下找到相關準則:https://github.com/isocpp/Cpp... 。指導準則試圖幫助程序員識別那些次優、容易出錯的表達方式,最終編寫出可讀性強、易於維護、簡單高效、類型安全和資源安全的代碼(http://www.stroustrup.com/res...)。這並非狂妄的想法!

這並不只僅是爲編程專家設計的。不管是專家仍是初學者,都應該瞭解運用工具支持檢測問題的觀點。工具支持的早期版本能夠在Visual Studio,Clang tidy和其餘地方找到。

事實上,咱們制定的指導準則已經受到了不少中高級編程人員的歡迎,他們把指導準則看成閱讀材料,學習如何更加高效地使用C++11和C++14。每個準則都有基本原理的支持,同時提供了正反面的代碼示例。

Guidelines Support Library的將來發展規劃是怎樣的?將來是否會像標準模板庫同樣,由主要的編譯器支持或是提供?

核心準則的目的是提供一種進一步利用C++的方式,能夠用來回答「將來5年代碼是什麼樣」的問題。利用C++11和C++14已經可以很好地編寫代碼了,但程序員個體忙於業務,沒有時間來評價新的工具,因此指導準側和工具支持就顯得頗有必要。

具體的支持有兩種形式:

  • 彌補ISO 標準庫不足的GSL

  • 幫助執行準則、提供準確性保證的靜態分析工具

GSL很小(也就十幾種類和函數),主要目的在於避免程序員直接使用C++當中最棘手、最不安全的部分。好比,C++裏有一種not_null 類型,確保指針不是nullptr,span能夠傳遞給函數(pointer, size) pair對象。

關於GSL在GCC、Clang和微軟的實現,能夠參見GitHub上麻省理工學院下的開源項目:https://github.com/Microsoft/GSL 。考慮到兼容問題,咱們正在努力實現GSL的標準樣式規範。

核心指導準則是ISO標準委員會部分紅員和其餘外部人士共同執行的項目,它不是標準委員的工做。雖然咱們已經向標準大會申請並但願某些GSL進入標準庫,但如今它們仍是各自獨立的。

相比較其餘程序員,高技能的程序員具備哪些品質?接觸編程學習較早,更加刻苦努力……

保持好奇心,願意終身學習下去;面對困難時,堅持不妥協;不止在編程方面,設計和電腦方面的基礎知識也必須堅實;樂於和系統用戶進行有效的溝通。

編程學習沒有「最佳年齡」或「最晚年齡」之說。若是你沒有在10歲、20歲或是30歲的時候開始接觸編程,這並不會影響你成爲偉大編程大師的可能。我20歲纔開始編程!重要的不只是成爲一名編程人員,你還要對本身設計的程序有感受,你要了解相關學科、領域的知識經驗。我認識的一些優秀編程人員並非計算機科學專業出身的:有學習數學的、工程的、歷史的、化學的、生物的,甚至還有哲學的。我認爲,真正重要的是,趁你還很年輕的時候,可以喜歡上某些學科,選擇具備挑戰性和感興趣的工做並養成良好的習慣。

我並不認爲一味地刻苦努力,全部成績拿A就是正確的方法。許多優秀的程序員是很是全面的人才,遺憾的是,並非全部的。

面對那些堅持「我不想知道如何彈鋼琴,只想知道如何像霍洛維茨同樣演奏」,急於尋求成功祕方的人,您的建議是?

霍洛維茨一輩子都在練習鋼琴演奏;若是你也想成爲編程界的霍洛維茨,就要決心用一生的時間去練習和學習。記住,「臺上一分鐘,臺下十年功。」霍洛維茨第一次公開演出以前,花了不少年的時間來練習。應該有15年的時間都在練習。

要成爲一名優秀的編程人員,你不須要是世界級的天才,也不用15年的編程學習,就能夠開發出實際的應用程序。但我建議你,在把本身的編程成果展現給別人看以前,須要花時間認真地學習和練習編程。

我確信,霍洛維茨是從手指練習開始的,選擇專門爲初學者編寫或是簡單的曲目練習。他沒有一開始就選擇李斯特的《匈牙利狂想曲》,沒有人上來就選擇最難的曲目。我猜測,不多有人在缺少堅實基礎知識的狀況下能達到高水平的成就。能夠奔跑,但要在學會中低水平的技能(走路)以後。


——更多訪談

更多精彩,加入圖靈訪談微信!

相關文章
相關標籤/搜索