1. 程式人生 > >很多新的語言來了又走,為什麼C++屹立30年?

很多新的語言來了又走,為什麼C++屹立30年?

C++已經問世30多年了。在此期間,很多新的語言來了又走,但是C++經得起考驗。今天介紹的這本新書《C++程式設計自學寶典》背後的一個大問題就是:為什麼選擇C++?答案就分佈於讀者將要看到的本書的內容中。但作為一個“攪局者”,C++是一門靈活、強大的語言,並且擁有豐富、龐大的標準庫提供支援。

C++一直是一門強大的語言,可以讓使用者直接訪問記憶體,同時提供大量的高階特性,比如建立新型別和類的能力,以及過載運算子以滿足使用者需求。然而,更現代的C++標準添加了不少特性:通過模板進行泛型程式設計,通過函式物件和lambda表示式進行函數語言程式設計。使用者可以根據需要充分地利用這些特性,也可以使用抽象介面指標或類C過程程式碼編寫事件驅動程式碼。

在本書中,我們將介紹C++11規範以及通過該語言提供的標準庫。本書使用簡短的程式碼片段解釋瞭如何使用這些特性,每一章包含一個實用示例來解釋這些概念。在本書的最後,讀者將瞭解該語言的所有功能以及C++標準庫可以實現的功能。假定讀者是初學者,本書將引導和提示讀者從零開始使用C++。

為什麼選擇C++?

​為什麼選擇C++?從讀者自身的實際情況來看,原因有很多。

讀者選擇C++可能是因為必須為一個C++專案提供技術支援。在超過30年的生命週期中,該專案中已經包含了數百萬行C++程式碼,並且大部分流行的應用程式和作業系統是使用C++編寫的,或者是使用了與之有關的元件和庫。幾乎不可能找到一臺不包含C++程式碼的電腦

或者讀者打算使用C++編寫新的程式碼。這可能是因為專案程式碼中將會用到一個使用C++編寫的程式庫,而且有成千上萬的程式庫可供選擇:開源的、共享的和商業軟體。

或者讀者可能是被C++強大的功能和靈活性所吸引。現代高階程式語言的目標是將程式設計師從繁複的程式設計工作中解放出來。同時,C++還允許使用者和機器保持儘可能緊密的聯絡,使得使用者可以直接訪問計算機記憶體(有時是比較危險的)。通過類和過載這些語言特性,C++是一門靈活的語言,我們可以對它進行功能擴充套件,並編寫可複用的程式碼。

不論讀者選擇C++的理由是什麼,這個決定都是非常明智的,本書可以作為讀者入門的起點。

本書講了什麼?

​本書是一本實用性的書,讀者可以對其中的程式碼輸入、編譯和執行。為了編譯程式碼,你將需要一個C++編譯器和連結器,在本書中它們是指提供Visual C++的Visual Studio 2017社群版程式。選擇該編譯器是因為我們可以免費下載它,它符合C++標準規範,並且包含大量能夠提高程式設計效率的工具。Visual C++提供了對C++11語言特性的支援,並幾乎相容C++14和C++17的所有語言特性。Visual C++還包含了C99執行時庫、C++11標準庫和C++14標準庫。上述所有規範意味著讀者在本書中將要學習的程式碼,將能夠被其他所有標準的C++編譯器編譯。

本文將從如何獲取和安裝Visual Studio 2017社群版程式的細節開始。如果你已經擁有了一個C++ 編譯器,那麼可以跳過本小節。本書大部分內容是與編譯器和連結器無關的。但是第10章介紹除錯和診斷技術時會涉及一些專屬於Microsoft的功能特性。Visual Studio是一款功能齊全的程式碼編輯器,所以即使你不使用它來管理專案檔案,也仍然會發現它對於編輯程式碼來說是非常有用的。

在介紹完程式安裝之後,讀者將學習一些C++的基礎知識:如何組織原始碼檔案和專案,以及如何管理可能存在幾千個檔案的專案。

最後,本文將以一個循序漸進的結構化示例作為結尾。這裡讀者將學習如何使用C++標準庫編寫簡單的函式以及一種管理專案檔案的機制。

C++的歷史

C++的前身是C語言,C語言是由Dennis Richie供職于貝爾實驗室時設計的,於1973年首次釋出。C語言曾經廣受青睞,並且用於編寫早期的Unix和Windows版本。事實上,大部分作業系統的程式庫和軟體開發包仍然包含C語言介面。C語言功能很強大,因為使用它編寫的程式碼可以被編譯成一種緊湊格式,它採用了靜態型別系統(因此編譯器可以進行型別檢查),並且該語言的型別和結構支援直接訪問記憶體的計算機架構。

不過C語言是過程式的並且基於函式,雖然它包含能夠封裝資料的記錄型別(struct),但是它不包含類似物件的行為來表現被封裝的狀態。顯然,使用者迫切希望有一種語言既擁有C語言的強大功能,又擁有面向物件的類的靈活性和可擴充套件性,也就是一種支援類的C語言。1983年,Bjarne Stroustrup發明了C++,++符號來自C語言的增量運算子++。

嚴格來說,在作為變數字尾時,++運算子表示變數執行自增操作,但返回的變數值是它執行自增操作之前的。因此在C語言程式碼語句“int c = 1; int d = c++;”當中,變數d獲得的返回值是1,變數c的值是2。從這一點來看,它並沒有明確地表達C++是C的增量這一理念。

C++專案結構

嚴格來說,在作為變數字尾時,++運算子表示變數執行自增操作,但返回的變數值是它執行自增操作之前的。因此在C語言程式碼語句“int c = 1; int d = c++;”當中,變數d獲得的返回值是1,變數c的值是2。從這一點來看,它並沒有明確地表達C++是C的增量這一理念。

C++專案中可以包含幾千個檔案,並且管理這些檔案甚至可以成為一個單獨的工作任務。當構建專案時,如果應該編譯某個檔案,那麼選擇哪種工具編譯它?檔案應該按照什麼順序編譯?這些編譯器生成的輸出結果又是什麼?編譯後的檔案應該如何組織到一起構造可執行檔案?

編譯器工具還擁有大量的選項,比如除錯資訊、優化型別、為多種語言特性提供支援以及處理器特性。編譯器選項的不同組合將會用於不同場景(比如版本構建和版本除錯)。如果使用者是在命令列上執行編譯任務的,那麼務必確保選擇了正確的選項,並在編譯所有原始碼的過程中始終應用它們。

檔案和編譯器選項的管理可以變得很複雜。這也是使用者應該使用一款構建工具處理即將上線的產品程式碼的原因。與Visual Studio一起安裝的構建工具有:MSBuild和nmake兩款。當用戶在Visual Studio環境下構建一個Visual C++專案時,將使用MSBuild,並且會把編譯規則存放在一個XML檔案中。使用者甚至可以在命令列中呼叫MSBuild,將XML專案檔案傳遞給它。nmake是Microsoft在多個編譯器之間維護程式多個版本的實用性工具。在本文中,讀者將學習如何充分利用nmake的實用性編寫一個簡單的makefile檔案。

在介紹專案管理的基礎知識之前,我們必須先了解使用者通常會在C++專案中找到哪些檔案以及編譯器會如何處理這些檔案。

編譯器

C++是一門高階程式語言,旨在為使用者提供豐富的語言特性,以及為使用者和其他開發人員提供良好的可讀性。計算機的處理器執行底層程式碼,並且這也是編譯器將C++程式碼轉化成處理器的機器碼的主要目的。單個編譯器也許可以相容多種處理器,如果程式碼是符合C++規範的,那麼它們還可以被其他編譯器編譯,以便相容其他處理器。

不過,編譯器的功能遠不止於此。C++允許使用者將程式碼分割成若干函式,這些函式可以接收引數並返回一個值,因此編譯器可以配置記憶體來傳遞這些資料。此外,函式可以宣告只在函式內部使用的變數,並且它將只在函式被呼叫時才存在。編譯器配置的記憶體稱為棧幀(stack frame)。編譯器中包含如何建立棧幀的選項,比如Microsoft的編譯器選項/Gd、/Gr和/Gz決定了函式引數被推送到堆疊上的次序,以及呼叫方函式或被呼叫函式在呼叫結束時是否應該從堆疊中移除這些引數。當我們編寫的程式碼需要和其他人共享時,這些選項將非常重要(不過基於本書的目的,應該會使用預設的堆疊結構)。這只是冰山一角,不過編譯器選項為使用者提供的強大功能和靈活性應該會讓讀者印象深刻。

編譯器編譯C++程式碼,如果遇到程式碼中的錯誤,將向用戶傳送編譯器錯誤提示資訊。它是對程式碼的語法檢查。這對於確保使用者從語法角度編寫完美的C++程式碼非常重要,不過這仍然可能是在做無用功。編譯器的語法檢查對於檢查程式碼來說非常重要,不過使用者應該總是使用其他方法檢查程式碼。比如下列程式碼聲明瞭一個整數型別變數併為它賦值:

int i = 1 / 0;

編譯器將向用戶提示C2124錯誤:divide or mod by zero(除數不能為0)。不過,下列程式碼將使用額外的變數執行相同的操作,但是編譯器不會報錯:

int j = 0;

int i = 1 / j;

當編譯器提示出現錯誤時將停止編譯。這意味兩件事:首先,你將無法得到編譯輸出結果,因此將不會在一個可執行檔案中找到該錯誤;其次,如果原始碼中存在其他錯誤,我們只有在修復當前錯誤重新編譯程式碼時才會發現它。如果你希望對程式碼執行語法檢查並退出編譯,可以使用/Zs選項開關。

編譯器還會生成警告資訊。一個警告意味著程式碼將被編譯,但是程式碼中的某個問題可能會對生成的可執行檔案產生潛在的不良影響。Microsoft編譯器將警告分為4個級別:級別1是最嚴重的(應該立刻解決),級別4是資訊性的。警告通常用於向用戶宣告被編譯的語言特性可以正常執行,不過它需要的某個特定編譯器選項,開發者並沒有使用。

在開發程式碼期間,我們將會經常忽略警告資訊,因為這可能是在測試某些語言特性。

不過,當開發的程式碼準備上線釋出時,你最好對警告資訊多加留意。預設情況下,Microsoft編譯器將顯示1級警告資訊,你可以使用/W選項和一個數字來宣告希望看到的警告資訊級別(比如,/W2表示使用者希望看到2級警告以及1級警告)。在正式上線的產品程式碼中,你可能會使用/Wx選項,這是告知編譯器將警告資訊也當作錯誤來看待,我們必須修復所有問題,以便能夠順利編譯程式碼。你還可以使用pragma編譯器(pragma的概念將稍後介紹),並且編譯器的選項還可以忽略特定警告資訊。

連結程式碼

編譯器將生成一個輸出。對於C++程式碼來說,這將是物件程式碼,不過你可能還會得到一些其他的編譯器輸出,比如被編譯的資原始檔。對於它們自身來說,這些檔案無法被執行,尤其是作業系統需要設定特定的結構時。一個C++專案將始終包含兩個階段:先將原始碼編譯成一個或者多個物件檔案,然後將這些物件檔案連結到一個可執行程式中。這意味著C++編譯器將提供另外一種工具,即連結器。

連結器也有決定它如何工作並指定輸出和輸入的選項供使用者選擇,並且它還會向我們發出錯誤和警告資訊。與編譯器類似,Microsoft的連結器也有一個選項/WX,它可以將預覽版程式中的警告資訊當作錯誤來處理。

本文摘自《C++程式設計自學寶典》

《C++程式設計自學寶典》

[英]理查德·格里姆斯 著

本書旨在通過全面細緻的內容和程式碼示例,帶領讀者更加全方位地認識C++語言。全書內容共計10章,由淺入深地介紹了C++的各項特性,包括C++語法、資料型別、指標、函式、類、面向物件特性、標準庫容器、字串、診斷和除錯等。本書涵蓋了C++11規範及相關的C++標準庫,是全面學習C++程式設計的合適之選。

邀請10名好友關注非同步圖書10天,即可免費獲得非同步新書。

點選閱讀原文,直接購買《C++程式設計自學寶典

閱讀原文