第2部分 與C++第一次親密接觸ios
在瀏覽了C++「三分天下」的世界版圖以後,便對C++有了基本的瞭解,算是一隻腳跨入了C++世界的大門。那麼,怎樣將咱們的另一隻腳也跨入C++世界的大門呢?是該即刻開始編寫C++程序?仍是……程序員
正在咱們猶豫的時候,便看到前面有一我的被一羣滿頭問號的C++初學者圍在當中。咱們趕忙擠進去一看,噢,原來是一個C++程序正作自我介紹呢。算法
「你們好,歡迎來到奇妙的C++世界。我是C++世界的迎賓——一個最簡單最普通的C++程序,個人名字叫「HelloWorld.exe」。我雖然簡單而普通,但卻幾乎是這個世界上最著名的C++程序。每一個來到C++世界的初學者與C++的第一次親密接觸都是經過我來完成的。你們在聽我介紹以前,必定會以爲C++程序很是神祕,好比,C++程序是如何建立的?一個完整的C++程序由哪幾部分構成?傳說中的源文件究竟是什麼文件?C++程序是如何執行的?其實,咱們C++程序一點都不神祕,跟你們同樣,咱們有本身的老爸老媽,有本身的五官四肢,也有本身的生命過程。什麼?你們以爲難以想象?彆着急,下面且聽我一一道來……」框架
你們進入C++世界最感興趣的第一件事,就是親自動手建立一個C++程序。大多數C++程序都是經過一種叫作集成開發環境(Integrated Development Environment,IDE)的軟件建立的,能夠說,它是建立C++程序的工廠。雖然能夠用於建立C++程序的集成開發環境有不少,可是咱們首選的仍是由微軟公司開發的Visual Studio。做爲一款通過二十多年不斷髮展而來的集成開發環境,Visual Studio擁有多種功能各異的版本:有適用於大規模團隊開發的旗艦版,也有適用於我的開發的專業版,還有免費的快速版。若是咱們只是想利用Visual Studio進行C++的學習,咱們可使用其中的Visual C++ Express版本。它不只功能齊備,並且小巧。更重要的是,它是免費的,咱們能夠從微軟的網站免費下載安裝。Visual Studio是Windows平臺上最經常使用的開發環境,至於其它的開發工具以及Linux平臺上的開發工具,請你們參考後文的2.3小節。函數
如今,請你們在個人引導下,一步一步地使用Visual Studio建立咱們的第一個C++程序。第0步,新建項目。從開始菜單找到已經安裝好的Visual Studio並點擊啓動。當第一次啓動Visual Studio時,它會要求咱們選擇界面佈局設置,這裏咱們選擇適用於Visual C++的佈局設置,而後通過片刻的等待就能夠看到Visual Studio華麗麗的起始頁了。在Visual Studio中,咱們全部的開發工做都是在某個項目中進行的,因此咱們利用Visual Studio編寫程序的第一步就是建立用於管理程序文件的項目:單擊起始頁左側的「新建項目」,在彈出的「新建項目」對話框中,選中左側樹狀圖中的「Visual C++」節點,而後在右側的項目類型列表中選中「空項目」這個項目模板,接着在對話框下方輸入項目名稱「HelloWorld」並選擇項目存放的位置,最後點擊「肯定」按鈕,就完成了新項目的建立。工具
圖2-1 新建項目佈局
第1步,添加源文件。新項目的建立,只是搭起了一個空的框架,還等着咱們向其中添加一些實質的內容。在左側的「解決方案資源管理器」中,找到「HelloWorld」項目下的「源文件」分支,而後右鍵單擊「源文件」分支,在彈出的菜單中依次選擇「添加->新建項」,就能夠獲得「添加新項」對話框。在「添加新項」對話框中,選中左側樹狀圖中的「Visual C++」分支,而後在右側文件類型列表中選中「C++文件」,接着在對話框下方將文件名稱修改成「HelloWorld.cpp」,最後點擊「肯定」按鈕,Visual Studio就會爲咱們新建一個HelloWorld.cpp源文件並添加到項目中。學習
圖2-2 添加源文件開發工具
第2步,編輯代碼。有了空白的源文件,就至關於畫家支好了畫板,做家擺好了稿紙,就等着咱們開始編寫代碼了。在已經被打開的HelloWorld.cpp文件中,咱們編輯以下的代碼(這裏值的特別提醒的是,其中的標點都應該是英文的):網站
#include <iostream> using namespace std; int main() { // 在屏幕輸出「Hello World!」字符串 cout<<"Hello World!"<<endl; return 0; }
圖2-3 編輯代碼
第3步,編譯執行程序。代碼編輯完成以後,咱們就能夠編譯並執行這個程序向C++世界打個招呼了。這一步能夠經過菜單命令「調試->開始執行(不調試)」來完成,但更多時候,咱們經過Ctrl+F5快捷鍵來完成。當咱們按下快捷鍵以後,Visual Studio會編譯項目當中的源文件,若是源文件中沒有錯誤,它就會生成相應的HelloWorld.exe可執行文件,隨後會啓動執行這個程序,從而在DOS窗口打印出一個「Hello World!」字符串向C++世界打招呼。
圖2-4 Hello World!
最佳實踐:等一下,等一下,我還沒看清輸出結果呢!
當咱們在Visual Studio中執行某個程序時,若是這個程序在執行過程當中不須要與用戶進行交互,那麼它執行時打開的DOS窗口會在其執行完畢後當即關閉。若是這個程序有結果輸出,咱們甚至來不及看清程序的輸出結果。一個程序執行完了,連輸出結果都來不及看清這怎麼行呢?
爲了解決這個問題,咱們能夠在程序的主函數返回以前加上一條「system("pause");」語句。例如:
int main() { // … // 讓程序在結束以前暫停 system("pause"); return 0; }
加上這條語句後,程序會在執行完畢以前暫停,這樣,咱們就有足夠的時間看清程序的輸出結果了。天然,這條語句也還能夠用在程序執行過程當中那些須要暫停的地方,以此來提升程序的可交互性。
另一種查看程序輸出結果的方法,就是先啓動DOS窗口,而後DOS窗口中手動地執行咱們的應用程序。在這種方式下,DOS窗口並不會在程序執行完畢後關閉,因此咱們也有時間查看程序的輸出結果。
通過這樣四個簡單的步驟,咱們就輕鬆編寫了咱們的第一個C++程序,完成了與C++的第一次親密接觸。除了藉助Visual Studio建立C++程序以外,咱們甚至還能夠採起純粹手工的方式建立C++程序。但不管何種方式,它們的基本流程都是同樣的。若是使用Visual Studio方式,由於有開發工具的幫助,上手容易開發效率也會比較高。而若是是使用手工方方式,則能夠對整個開發過程進行靈活定製,從而知足咱們一些個性化的需求。對於初學者而言,手工方式稍顯複雜。最佳的學習路線應該是,先以Visual Studio方式入門,等到有了必定的基礎,須要對程序的編寫過程有更精細控制的時候,再改用手工方式。這樣纔不至於在入門階段就被複雜的手工方式困住了腳步,而在進階後又受到開發工具的限制。
麻雀雖小,五臟俱全。你們別看我個頭小,只有短短的幾行代碼,實現的功能也很簡單,可是我一樣擁有C++程序的「五官和四肢」:預編譯指令、程序代碼和註釋,如圖2-5所示。大多數狀況下,這三個基本組成部分都被放在一個擴展名爲「cpp」的文本文件中,這個文件被稱爲C++ 源文件。源文件記錄了個人「五官和四肢」,規劃了個人人生。源文件的編寫者就是個人設計師了。經過修改源文件,能夠改變個人面貌、個人人生軌跡,讓我完成各類任務,實現各類功能。
圖2-5 C++程序=預編譯指令+程序代碼+註釋
下面,你們一塊兒來看看個人源文件,從中認識個人「五官和四肢」。
在源文件中,以「#」開始的內容就是預編譯指令。它的做用是告訴編譯器,讓它在真正進行編譯以前對源文件進行一些插入文件、替換字符串等預處理,以獲得最終參與編譯的源文件。例如,在個人源文件HelloWorld.cpp中,第一行就是一個插入文件的預編譯指令:
#include <iostream>
其中,「#include」指令用於將指定的文件插入該指令所在的位置,做爲整個源文件的一部分。由於這樣的文件老是在一個源文件的頭部被插入,因此咱們一般將這樣的文件稱爲頭文件(header file)。在這裏,咱們插入了「iostream」這個頭文件,這是由於咱們在程序中須要用到其中定義的cout和endl來完成輸出(關於C++的輸入輸出,能夠參考後文2.2小節的介紹)。須要注意的是,「#include」指令後的文件名有兩種表示方式:若是使用雙引號""來包圍一個文件名,則預處理器在處理這個指令的時候,將首先在當前目錄(也就是這個源文件所在的目錄)下搜索這個文件,若是不存在,則繼續在項目的包含目錄(包括項目的默認頭文件目錄,也就是Visual Studio安裝目錄下的「\VC\include」文件夾,以及在項目屬性中設置的項目附加頭文件目錄)下搜索這個文件;而若是使用尖括號<>來包圍一個文件名,預處理器則會直接在項目的包含目錄下搜索這個文件。因此,一般咱們使用""來插入當前項目目錄下的頭文件(好比咱們本身建立的頭文件),而使用<>來插入各類項目包含目錄下的庫頭文件(好比這裏的iostream)。這裏值得再次提醒的是,代碼中使用的全部標點符號(這裏使用的尖括號,也包括後面代碼中用到的雙引號、逗號、分號等)必須是英文的。某些中英文符號很是類似,很容易被初學者搞混淆而引發編譯錯誤,這一點尤爲值得初學者注意。
程序代碼主體由若干C++語句(一般以分號結束的一行代碼就是一條語句)構成,能夠說語句是構成程序的基本單位。在個人源文件中,第一條C++語句是:
using namespace std;
這條語句表示我所使用的名字空間是std。所謂名字空間,就是程序中各類標識符(好比這裏的cout和endl,咱們經過這些符號來訪問程序中的各類元素,實際上也能夠說是這些元素的名字,因此這些符號也被稱爲標識符)所在的範圍,更具體的能夠參考7.3.2小節關於名字空間的介紹。在C++中,任何標識符都被定義在某個名字空間中,而同一個標識符也能夠在多個名字空間中同時定義。這就像張家村有一我的叫陳良喬,而李家村也有一我的叫陳良喬同樣,你們在稱呼「陳良喬」這我的的時候,爲了表達清楚,咱們必須在名字前加上一個前綴,稱之爲「張家村的陳良喬」或是「李家村的陳良喬」。C++中的名字空間就至關於這裏的「張家村」、「李家村」。在這裏,咱們在後面代碼中使用的cout、endl都是來自std這個村的,因此咱們用這條語句告訴編譯器,若是遇到沒有加前綴的標識符(好比,這裏的cout),能夠到std村去找找看,若是能找到,那就是它了(結果,在std村裏找到了std::cout)。C++的大多數內容都定義在std這個名字空間中,因此,不少時候咱們都須要在代碼中使用這條語句引入std名字空間。固然,若是不使用這條語句,咱們也能夠在標識符的前面直接加上名字空間的前綴,明確地表示這是來自於某個名字空間的標識符(在代碼中,使用 std::cout代替 cout,用std::endl代替endl)。
接下來的一條語句是:
int main()
這條語句連同它後面大括號內的內容,共同構成了main()函數,也稱爲主函數。所謂函數,是C++程序中最基本的一個組織單元,它把若干條語句組織到一塊兒共同實現某個功能。若是說一條語句至關於人體的一個細胞的話,那麼函數就至關於由若干細胞構成的擁有必定功能的器官。而這裏的主函數就是一個程序中最重要的「器官」。一個C++程序必須有一個主函數,且只能有一個主函數。當C++程序開始執行的時候,將首先進入主函數,而後逐條地執行其中的語句,直到其中的語句執行完畢退出主函數,程序執行也就宣告結束。能夠說,主函數定義了一個C++程序的一輩子。
知道更多:爲何一個C++程序必須有且只能有一個main函數?
當咱們雙擊執行程序後,執行程序的進程會首先建立主線程,主線程而後調用約定啓動運行時庫,由啓動運行時庫調用約定好的main函數,自此開始執行用戶的代碼。main函數是主線程的執行入口,因此一個C++程序必須有一個main函數。
同時,一個線程不可能擁有多個執行入口。並且在C++中,全局符號(變量、函數) 只能有一個定義。main函數做爲一個全局函數,天然也就只能有一個。
接下來的就是主函數中的一條語句:
cout<<"Hello World!"<<endl;
cout是定義在頭文件「iostream」中的一個輸出流對象,它是C++標準庫預約義的對象,一般用於將文字或數字輸出到屏幕。前面使用「#include」預編譯指令包含「iostream」頭文件就是爲了在代碼中使用這個對象。關於輸入/輸出流,會在之後的章節中作更詳細的介紹,這裏只要知道這條語句能夠將「Hello World!」這串文字輸出到屏幕上便可。值得再次提醒的是,這裏的雙引號也必須是英文的。
個人最後一條語句是:
return 0;
它表示程序成功執行完畢並返回(return)。一般,咱們返回一個0值表示程序成功執行(若是在程序的執行過程當中出現錯誤,也能夠返回表示錯誤信息的其餘數值。程序的執行者能夠接受這個返回值以判斷程序是否成功執行)。到這裏,主函數中的語句執行完畢,而個人一輩子也到此終結。
註釋是源代碼的編寫者爲了幫助代碼的閱讀者(代碼後期維護人員,也包括編寫者本身)更好地理解代碼,而在代碼中寫下的關於某一行或某一段代碼的一些解釋性文字。雖然源代碼中的註釋並不會參與最終的編譯,不會對程序的功能產生影響,但它會提升代碼的可讀性,爲後期的維護帶來極大的便利。例如這裏的:
// 在屏幕輸出「Hello World!」字符串
就是一條註釋,它解釋了接下來的一條語句的做用,從而可讓咱們對代碼有更好的理解。
在形式上,C++中的註釋能夠分爲單行註釋和塊註釋兩種。「//」是單行註釋符,「//」以後直到換行的全部內容都屬於註釋。由於內容只有一行,因此它經常用來對代碼做簡短的解釋。例如上面的註釋就是一個典型的單行註釋。
C++中的塊註釋咱們用一對「/*」和「*/」表示,凡是出如今這對符號之間的全部內容都屬於註釋。由於它能夠包含多行內容,當咱們須要對代碼作詳細解釋的時候,可使用塊註釋。例如:
/* 這是一段註釋 */
在功能上,註釋通常分爲序言性註釋和解釋性註釋。序言性註釋多位於程序源文件的開始,用來講明程序的文件名、用途、編寫時間、維護歷史等。在上面的例子中,咱們能夠在源文件的第一行加上一個序言性註釋來解釋這個源文件的功能:
// HelloWorld.cpp:在屏幕輸出「Hello World!」字符串
序言性註釋被普遍用於大型的項目中。一般,每一個項目都有本身定義的序言性註釋格式,用來向代碼的閱讀者說明一些必要的信息。下面是從一個實際的項目中摘錄的一段序言性註釋,它說明了源文件的名字、做用、文件的修改歷史等信息,幫助閱讀者更好地理解代碼。你們能夠以此爲模板,編寫適合於本身的序言性註釋。
/////////////////////////////////////////////////////////////////////// // AppDataView.cpp : implementation file // //CAppDataView // This view is designed to display the App Data // // Version: 2.1 // Date: September 2001 // Author: Chen Liangqiao // Email: chenlq@live.com // Copyright (c) 2002. All Rights Reserved. // // History: /* 27.09.2001 Chen Liangqiao Added OnCreate(), OnUpdate(): Added usage of mesh tracer layers Added bugfix for Graphics zoom error 30.10.2001 Chen Liangqiao Changed order of MPR View only in _TORCHTONAV 08.11.2001 Zeng Me Added EUpdateReason, used for UpdateAllView(), Added voxel trafo Changed the control panel due to new CTestCtrl */ ///////////////////////////////////////////////////////////////////////
與序言性註釋多位於源文件開始部分不一樣,解釋性註釋多分散於源代碼的各個部分,用來向代碼閱讀者解釋代碼的含義,說明一些必要的問題等。例如,上面例子中的註釋:
//在屏幕上輸出「Hello World!」字符串 cout<<"Hello World!"<<endl;
這句解釋性註釋就是用來向代碼閱讀者說明其下代碼的功能是輸出字符串「Hello World!」。
最佳實踐:什麼是好的註釋
雖然程序的註釋並不影響程序功能的實現,編譯器也不會去閱讀咱們的註釋,可是好的註釋卻能夠增長程序代碼的可讀性,使程序更易於維護。誰都不肯意維護一份沒有註釋的代碼,那無異於閱讀天書。那麼,什麼樣的註釋纔算是好註釋呢?
首先,該註釋的地方必定要註釋。
註釋是對代碼的「提示和說明」,是爲了幫助代碼的閱讀者更好地理解代碼而存在的。當咱們認爲代碼不能被「一眼看穿」而須要加以解釋,或者是代碼須要特別說明的時候,就應該添加註釋,加以額外的解釋和說明,幫助閱讀者理解代碼。例如:
// 判斷某個浮點數是否近似整數 bool is_int(double d) { // 用浮點數d減去其整數部分(int)d,得到其小數部分 double s = d - (int)d; // 判斷小數部分是否在偏差範圍內 if(s > 0.000001) return false; else return true; }
這裏的註釋,恰當地對比較難以理解的代碼進行了解釋(若是沒有註釋,很難一會兒就理解「double s = d - (int)d;」這行代碼的含義究竟是什麼),提升了代碼的可讀性。
其次,不應註釋的地方最好不要註釋。
註釋僅僅是對代碼的「提示和說明」而已,若是代碼自己已經可以很好地作到「望文生義」,也就沒有必要「多此一舉」地加以註釋了。另外須要注意的是,註釋只是簡短的說明性文字,不是詳盡的文檔。程序的註釋不可喧賓奪主,註釋過多會讓人眼花繚亂,反而下降了代碼的可讀性。例如,下面代碼中的註釋就不太合適:
// 判斷某個浮點數是否近似整數 // 其參數是表示輸入的浮點數d // 其返回值是一個表示是否近似的bool值 bool is_int(double d) { // 用浮點數減去其整數部分,得到其小數部分 // 其中,d表示浮點數,(int)d表示浮點數的整數部分 double s = d - (int)d; // 判斷小數部分是否在偏差範圍內 if(s > 0.000001) return false; // 小數部分大於偏差範圍,則表示浮點數不近似整數,返回false else return true; // 小數部分小於偏差範圍,則表示浮點數近似整數,返回true }
這段代碼中的註釋,對一些含義很是淺顯易懂的代碼也進行了詳盡的解釋,註釋的內容遠超過了代碼的內容,這樣不但沒有增長代碼的可讀性,反卻是讓代碼淹沒在了複雜的註釋中,反而下降了代碼的可讀性。這樣的註釋實屬「多此一舉」畫蛇添足。
另外,應該養成良好的代碼註釋習慣。編寫代碼時添加必要的註釋,修改代碼時修改相應的註釋,刪除無用的註釋,保證註釋與代碼的一致性。
註釋應當準確、易懂,避免二義性。錯誤的註釋不但無益反而有害。
註釋的位置應與被描述的代碼相鄰,能夠放在代碼的上方或右方,不可放在下方。例如:
// 在屏幕輸出「Hello World!」字符串 // 對下方的代碼進行註釋 cout<<"Hello World!"<<endl; int n = 1024; // 循環次數,對左側代碼進行註釋
若是代碼比較長,特別是有多重嵌套時,應當在某些段落的結束處加以註釋,以便於查看嵌套結構的起始和結束位置。例如,一個多重循環的代碼及其註釋以下:
for ( int i = 0; i < 100; ++i ) { for ( int j = 0; j < 100; ++j ) { // 算法處理... } // j循環結束 } // i循環結束
程序代碼不只僅是寫給編譯器看的,它更是寫給程序員本身或者他人看的。對於編譯器來講,代碼中有沒有註釋無所謂,然而對於閱讀代碼的程序員來講,合適的註釋能夠很大程度上提升代碼的可讀性,讓代碼更易於維護。於是,註釋是C++程序代碼中必不可少的一部分,而程序代碼中是否包含合適的註釋,也成爲衡量一個程序員是否優秀的標準。
預編譯指令、程序代碼與註釋共同構成了個人「五官與四肢」,但這時候我還只是一個後綴爲cpp的文本文件,而要獲得最後的可執行的exe文件,還得靠個人父親母親:編譯器和連接器。