申明: 本站飛宇網 https://feiyetopro.blogspot.com/。自網路收集整理之書籍、文章、影音僅供預覽交流學習研究,其[書籍、文章、影音]情節內容, 評論屬其個人行為, 與本網站無關。版權歸原作者和出版社所有,請在下載 24 小時內刪除,不得用作商業用途;如果您喜歡其作品,請支持訂閱購買[正版]。謝謝!
C++ Primer Plus(第6版)中文版
第2章 開始學習C++
本章內容包括:
- 創建C++程式。
- C++程式的一般格式。
- #include編譯指令。
- main( )函數。
- 使用cout物件進行輸出。
- 在C++程式中加入注釋。
- 何時以及如何使用endl。
- 聲明和使用變數。
- 使用cin物件進行輸入。
- 定義和使用簡單函數。
要建造簡單的房屋,首先要打地基、搭框架。如果一開始沒有牢固的結構,後面就很難建造窗子、門框、圓屋頂和鑲木地板的舞廳等。同樣,學習電腦語言時,應從程式的基本結構開始學起。只有這樣,才能一步一步瞭解其具體細節,如迴圈和對象等。本章對C++程式的基本結構做一概述,並預覽後面將介紹的主題,如函數和類。(這裡的理念是,先介紹一些基本概念,這樣可以激發讀者接下去學習的興趣。)
2.1 進入C++
首先介紹一個顯示消息的簡單C++程式。程式清單2.1使用C++工具cout生成字元輸出。原始程式碼中包含一些供讀者閱讀的注釋,這些注釋都以//打頭,編譯器將忽略它們。C++對大小寫敏感,也就是說區分大寫字元和小寫字元。這意味著大小寫必須與示例中相同。例如,該程式使用的是cout,如果將其替換為Cout或COUT,程式將無法通過編譯,並且編譯器將指出使用了未知的識別字(編譯器也是對拼寫敏感的,因此請不要使用kout或coot)。檔副檔名cpp是一種表示C++程式的常用方式,您可能需要使用第1章介紹的其他副檔名。
程式清單2.1 myfirst.cpp
程式調整
要在自己的系統上運行本書的示例,可能需要對其進行修改。有些視窗環境在獨立的視窗中運行程式,並在程式運行完畢後自動關閉該視窗。正如第1章討論的,要讓視窗一直打開,直到您按任何鍵,可在return語句前添加如下語句:對於有些程式,要讓視窗一直打開,直到您按任何鍵,必須添加兩條這樣的語句。第4章將更詳細地介紹cin.get( )。如果您使用的系統很舊,它可能不支援C++98新增的特性。有些程式要求編譯器對C++11標準提供一定的支援。對於這樣的程式,將明確的指出這一點,並在可能的情況下提供非C++11代碼。
將該程式複製到您選擇的編輯器中(或使用本書配套網站的原始程式碼,詳情請參閱封底)後,便可以C++編譯器創建可執行代碼了(參見第1章的介紹)。下面是運行編譯後的程式時得到的輸出:
C語言輸入和輸出
如果已經使用過C語言進行程式設計,則看到cout函數(而不是printf( )函數)時可能會小吃一驚。事實上,C++能夠使用printf( )、scanf( )和其他所有標準C輸入和輸出函數,只需要包含常規C語言的stdio.h文件。不過本書介紹的是C++,所以將使用C++的輸入工具,它們在C版本的基礎上作了很多改進。
您使用函數來創建C++程式。通常,先將程式組織為主要任務,然後設計獨立的函數來處理這些任務。程式清單2.1中的示例非常簡單,只包含一個名為main( )的函數。myfirst.cpp示例包含下述元素。
- 注釋,由首碼//標識。
- 前置處理器編譯指令#include。
- 函數頭:int main( )。
- 編譯指令using namespace。
- 函數體,用{和}括起。
- 使用C++的cout工具顯示消息的語句。
- 結束main( )函數的return語句。
下面詳細介紹這些元素。先來看看main( )函數,因為瞭解了main( )的作用後,main( )前面的一些特性(如前置處理器編譯指令)將更易於理解。
2.1.1 main( )函數
去掉修飾後,程式清單2.1中的示例程式的基本結構如下:
這幾行表明有一個名為main( )的函數,並描述了該函數的行為。這幾行代碼構成了函式定義(function definition)。該定義由兩部分組成:第一行int main( )叫函數頭(function heading),花括弧({和})中包括的部分叫函數體。圖2.1對main( )函數做了說明。函數頭對函數與程式其他部分之間的介面進行了總結;函數體是指出函數應做什麼的電腦指令。在C++中,每條完整的指令都稱為語句。所有的語句都以分號結束,因此在輸入示例代碼時,請不要省略分號。
圖2.1 main( )函數
main( )中最後一條語句叫做返回語句(return statement),它結束該函數。本章將講述有關返回語句的更多知識。
語句和分號
語句是要執行的操作。為理解原始程式碼,編譯器需要知道一條語句何時結束,另一條語句何時開始。有些語言使用語句分隔符號。例如,FORTRAN通過行尾將語句分隔開來,Pascal使用分號分隔語句。在Pascal中,有些情況下可以省略分號,例如END前的語句後面,這種情況下,實際上並沒有將兩條語句分開。不過C++與C一樣,也使用結束字元(terminator),而不是分隔符號。結束字元是一個分號,它是語句的結束標記,是語句的組成部分,而不是語句之間的標記。結論是:在C++中,不能省略分號。
1.作為介面的函數頭
就目前而言,需要記住的主要一點是,C++句法要求main( )函數的定義以函數頭int main( )開始。本章後面的“函數”一節將詳細討論函數頭句法,然而,為滿足讀者的好奇心,下面先預覽一下。
通常,C++函數可被其他函數啟動或調用,函數頭描述了函數與調用它的函數之間的介面。位於函數名前面的部分叫做函數返回類型,它描述的是從函數返回給調用它的函數的資訊。函數名後括弧中的部分叫做形參清單(argument list)或參數列表(parameter list);它描述的是從調用函數傳遞給被調用的函數的資訊。這種通用格式用於main( )時讓人感到有些迷惑,因為通常並不從程式的其他部分調用main( )。
然而,通常,main( )被啟動代碼調用,而啟動代碼是由編譯器添加到程式中的,是程式和作業系統(UNIX、Windows 7或其他作業系統)之間的橋樑。事實上,該函數頭描述的是main( )和作業系統之間的介面。
來看一下main( )的介面描述,該介面從int開始。C++函數可以給調用函數返回一個值,這個值叫做返回值(return value)。在這裡,從關鍵字int可知,main( )返回一個整數值。接下來,是空括弧。通常,C++函數在調用另一個函數時,可以將資訊傳遞給該函數。括弧中的函數頭部分描述的就是這種資訊。在這裡,空括弧意味著main( )函數不接受任何資訊,或者main( )不接受任何參數。(main( )不接受任何參數並不意味著main( )是不講道理的、發號施令的函數。相反,術語參數(argument)只是電腦人員用來表示從一個函數傳遞給另一個函數的資訊)。
簡而言之,下面的函數頭表明main( )函數可以給調用它的函數返回一個整數值,且不從調用它的函數那裡獲得任何資訊:
很多現有的程式都使用經典C函數頭:
在C語言中,省略返回類型相當於說函數的類型為int。然而,C++逐步淘汰了這種用法。
也可以使用下面的變體:
在括弧中使用關鍵字void明確地指出,函數不接受任何參數。在C++(不是C)中,讓括弧空著與在括弧中使用void等效(在C中,讓括弧空著意味著對是否接受參數保持沉默)。
有些程式師使用下面的函數頭,並省略返回語句:
這在邏輯上是一致的,因為void返回類型意味著函數不返回任何值。該變體適用於很多系統,但由於它不是當前標準強制的一個選項,因此在有些系統上不能工作。因此,讀者應避免使用這種格式,而應使用C++標準格式,這不需要做太多的工作就能完成。
最後,ANSI/ISO C++標準對那些抱怨必須在main( )函數最後包含一條返回語句過於繁瑣的人做出了讓步。如果編譯器到達main( )函數末尾時沒有遇到返回語句,則認為main( )函數以如下語句結尾:
這條隱含的返回語句只適用於main( )函數,而不適用於其他函數。
2.為什麼main( )不能使用其他名稱
之所以將myfirst.cpp程式中的函數命名為main( ),原因是必須這樣做。通常,C++程式必須包含一個名為main( )的函數(不是Main( )、MAIN( )或mane( )。記住,大小寫和拼寫都要正確)。由於myfirst.cpp程式只有一個函數,因此該函數必須擔負起main( )的責任。在運行C++程式時,通常從main( )函數開始執行。因此,如果沒有main( ),程式將不完整,編譯器將指出未定義main( )函數。
存在一些例外情況。例如,在Windows程式設計中,可以編寫一個動態連結程式庫(DLL)模組,這是其他Windows程式可以使用的代碼。由於DLL模組不是獨立的程式,因此不需要main( )。用於專用環境的程式—如機器人中的控制器晶片—可能不需要main( )。有些程式設計環境提供一個框架程式,該程式調用一些非標準函數,如_tmain( )。在這種情況下,有一個隱藏的main( ),它調用_tmain( )。但常規的獨立程式都需要main( ),本書討論的都是這種程式。
2.1.2 C++注釋
C++注釋以雙斜杠(//)打頭。注釋是程式師為讀者提供的說明,通常標識程式的一部分或解釋代碼的某個方面。編譯器忽略注釋,畢竟,它對C++的瞭解至少和程式師一樣,在任何情況下,它都不能理解注釋。對編譯器而言,程式清單2.1就像沒有注釋一樣:
本書所有的程式都以注釋開始,這些注釋指出了原始程式碼的檔案名並簡要地總結了該程式。在第1章中介紹過,原始程式碼的檔副檔名取決於所用的C++系統。在其他系統中,檔案名可能為myfirst.C或myfirst.cxx。
提示:
應使用注釋來說明程式。程式越複雜,注釋的價值越大。注釋不僅有助於他人理解這些代碼,也有助於程式師自己理解代碼,特別是隔了一段時間沒有接觸該程式的情況下。
C-風格注釋
C++也能夠識別C注釋,C注釋包括在符號/和/之間:由於C-風格注釋以*/結束,而不是到行尾結束,因此可以跨越多行。可以在程式中使用C或C++風格的注釋,也可以同時使用這兩種注釋。但應儘量使用C++注釋,因為這不涉及到結尾符號與起始符號的正確配對,所以它產生問題的可能性很小。事實上,C99標準也在C語言中添加了//注釋。
2.1.3 C++前置處理器和iostream文件
下面簡要介紹一下需要知道的一些知識。如果程式要使用C++輸入或輸出工具,請提供這樣兩行代碼:
可使用其他代碼替換第2行,這裡使用這行代碼旨在簡化該程式(如果編譯器不接受這幾行代碼,則說明它沒有遵守標準C++98,使用它來編譯本書的示例時,將出現眾多其他的問題)。為使程式正常工作,只需要知道這些。下面更深入地介紹一下這些內容。
C++和C一樣,也使用一個前置處理器,該程式在進行主編譯之前對原始檔案進行處理(第1章介紹過,有些C++實現使用翻譯器程式將C++程式轉換為C程式。雖然翻譯器也是一種前置處理器,但這裡不討論這種前置處理器,而只討論這樣的前置處理器,即它處理名稱以#開頭的編譯指令)。不必執行任何特殊的操作來調用該前置處理器,它會在編譯器時自動運行。
程式清單2.1使用了#include編譯指令:
該編譯指令導致前置處理器將iostream檔的內容添加到程式中。這是一種典型的前置處理器操作:在原始程式碼被編譯之前,替換或添加文本。
這提出了一個問題:為什麼要將iostream檔的內容添加到程式中呢?答案涉及程式與外部世界之間的通信。iostream中的io指的是輸入(進入程式的資訊)和輸出(從程式中發送出去的資訊)。C++的輸入/輸出方案涉及iostream檔中的多個定義。為了使用cout來顯示消息,第一個程式需要這些定義。#include編譯指令導致iostream檔的內容隨原始程式碼檔的內容一起被發送給編譯器。實際上,iostream檔的內容將取代程式中的代碼行#include <iostream>。原始檔沒有被修改,而是將原始程式碼檔和iostream組合成一個複合檔案,編譯的下一階段將使用該檔。
注意:
使用cin和cout進行輸入和輸出的程式必須包含檔iostream。
2.1.4 標頭檔名
像iostream這樣的檔叫做包含檔(include file)—由於它們被包含在其他檔中;也叫標頭檔(header file)—由於它們被包含在檔起始處。C++編譯器自帶了很多頭檔,每個頭檔都支援一組特定的工具。C語言的傳統是,標頭檔使用副檔名h,將其作為一種通過名稱標識檔案類型的簡單方式。例如,標頭檔math.h支援各種C語言數學函數,但C++的用法變了。現在,對老式C的標頭檔保留了副檔名h(C++程式仍可以使用這種檔),而C++標頭檔則沒有副檔名。有些C標頭檔被轉換為C++標頭檔,這些檔被重新命名,去掉了副檔名h(使之成為C++風格的名稱),並在檔案名稱前面加上首碼c(表明來自C語言)。例如,C++版本的math.h為cmath。有時C標頭檔的C版本和C++版本相同,而有時候新版本做了一些修改。對於純粹的C++標頭檔(如iostream)來說,去掉h不只是形式上的變化,沒有h的標頭檔也可以包含名稱空間—本章的下一個主題。表2.1對標頭檔的命名約定進行了總結。
表2.1 標頭檔命名約定
標頭檔類型
|
約 定
|
示 例
|
說 明
|
|---|---|---|---|
C++舊式風格
|
以.h結尾
|
iostream.h
|
C++程式可以使用
|
C舊式風格
|
以.h結尾
|
math.h
|
C、C++程式可以使用
|
C++新式風格
|
沒有副檔名
|
iostream
|
C++程式可以使用,使用namespace std
|
轉換後的C
|
加上首碼c,沒有副檔名
|
cmath
|
C++程式可以使用,可以使用不是C的特性,如namespace std
|
由於C使用不同的檔副檔名來表示不同檔案類型,因此用一些特殊的副檔名(如.hpp或.hxx)表示C++標頭檔是有道理的,ANSI/ISO委員會也這樣認為。問題在於究竟使用哪種副檔名,因此最終他們一致同意不使用任何副檔名。
2.1.5 名稱空間
如果使用iostream,而不是iostream.h,則應使用下面的名稱空間編譯指令來使iostream中的定義對程式可用:
名稱空間支援是一項C++特性,旨在讓您編寫大型程式以及將多個廠商現有的代碼組合起來的程式時更容易,它還有助於組織程式。一個潛在的問題是,可能使用兩個已封裝好的產品,而它們都包含一個名為wanda( )的函數。這樣,使用wanda( )函數時,編譯器將不知道指的是哪個版本。名稱空間讓廠商能夠將其產品封裝在一個叫做名稱空間的單元中,這樣就可以用名稱空間的名稱來指出想使用哪個廠商的產品。因此,Microflop Industries可以將其定義放到一個名為Microflop的名稱空間中。這樣,其wanda( )函數的全稱為Microflop::wanda( );同樣,Piscine公司的wanda( )版本可以表示為Piscine::wanda( )。這樣,程式就可以使用名稱空間來區分不同的版本了:
按照這種方式,類、函數和變數便是C++編譯器的標準元件,它們現在都被放置在名稱空間std中。僅當標頭檔沒有副檔名h時,情況才是如此。這意味著在iostream中定義的用於輸出的cout變數實際上是std::cout,而endl實際上是std::endl。因此,可以省略編譯指令using,以下述方式進行編碼:
然而,多數用戶並不喜歡將引入名稱空間之前的代碼(使用iostream.h和cout)轉換為名稱空間代碼(使用iostream和std::cout),除非他們可以不費力地完成這種轉換。於是,using編譯指令應運而生。下面的一行代碼表明,可以使用std名稱空間中定義的名稱,而不必使用std::首碼:
然而,要使用iostream中的其他名稱,必須將它們分別加到using列表中。本書首先採用這種偷懶的方法,其原因有兩個。首先,對於簡單程式而言,採用何種名稱空間管理方法無關緊要;其次,本書的重點是介紹C++的基本方面。本書後面將採用其他名稱空間管理技術。
2.1.6 使用cout進行C++輸出
現在來看一看如何顯示消息。myfirst.cpp程式使用下面的C++語句:
雙引號括起的部分是要列印的消息。在C++中,用雙引號括起的一系列字元叫做字串,因為它是由若干字元組合而成的。<<符號表示該語句將把這個字串發送給cout;該符號指出了資訊流動的路徑。cout是什麼呢?它是一個預定義的物件,知道如何顯示字串、數位和單個字元等(第1章介紹過,物件是類的特定實例,而類定義了資料的存儲和使用方式)。
馬上就使用物件可能有些困難,因為幾章後才會介紹物件。實際上,這演示了物件的長處之一—不用瞭解物件的內部情況,就可以使用它。只需要知道它的介面,即如何使用它。cout物件有一個簡單的介面,如果string是一個字串,則下面的代碼將顯示該字串:
對於顯示字串而言,只需知道這些即可。然而,現在來看看C++從概念上如何解釋這個過程。從概念上看,輸出是一個流,即從程式流出的一系列字元。cout物件表示這種流,其屬性是在iostream檔中定義的。cout的物件屬性包括一個插入運算子(<<),它可以將其右側的資訊插入到流中。請看下面的語句(注意結尾的分號):
它將字串“Come up and C++ me some time.”插入到輸出流中。因此,與其說程式顯示了一條消息,不如說它將一個字串插入到了輸出流中。不知道為什麼,後者聽起來更好一點(參見圖2.2)。
圖2.2 使用cout顯示字串
初識運算子重載
如果熟悉C後才開始學習C++,則可能注意到了,插入運算子(<<)看上去就像按位左移運算子(<<),這是一個運算子重載的例子,通過重載,同一個運算子將有不同的含義。編譯器通過上下文來確定運算子的含義。C本身也有一些運算子重載的情況。例如,&符號既表示位址運算子,又表示按位AND運算子;* 既表示乘法,又表示對指針解除引用。這裡重要的不是這些運算子的具體功能,而是同一個符號可以有多種含義,而編譯器可以根據上下文來確定其含義(這和確定“sound card”中的“sound”與“sound financial basic”中的“sound”的含義是一樣的)。C++擴展了運算子重載的概念,允許為用戶定義的類型(類)重新定義運算子的含義。
1.控制符endl
現在來看看程式清單2.1中第二個輸出流中看起來有些古怪的符號:
endl是一個特殊的C++符號,表示一個重要的概念:重起一行。在輸出流中插入endl將導致螢幕游標移到下一行開頭。諸如endl等對於cout來說有特殊含義的特殊符號被稱為控制符(manipulator)。和cout一樣,endl也是在標頭檔iostream中定義的,且位於名稱空間std中。
列印字串時,cout不會自動移到下一行,因此在程式清單2.1中,第一條cout語句將游標留在輸出字串的後面。每條cout語句的輸出從前一個輸出的末尾開始,因此如果省略程式清單2.1中的endl,得到的輸出將如下:
從上述輸出可知,Y緊跟在句點後面。下面來看另一個例子,假設有如下代碼:
同樣,每個字串緊接在前一個字串的後面。如果要在兩個字串之間留一個空格,必須將空格包含在字串中。注意,要嘗試上述輸出示例,必須將代碼放到完整的程式中,該套裝程式含一個main( )函數頭以及起始和結束花括弧。
2.分行符號
C++還提供了另一種在輸出中指示換行的舊式方法:C語言符號\n:
\n被視為一個字元,名為分行符號。
顯示字串時,在字串中包含分行符號,而不是在末尾加上endl,可減少輸入量:
另一方面,如果要生成一個空行,則兩種方法的輸入量相同,但對大多數人而言,輸入endl更為方便:
本書中顯示用引號括起的字串時,通常使用分行符號\n,在其他情況下則使用控制符endl。一個差別是,endl確保程式繼續運行前刷新輸出(將其立即顯示在螢幕上);而使用“\n”不能提供這樣的保證,這意味著在有些系統中,有時可能在您輸入資訊後才會出現提示。
分行符號是一種被稱為“轉義序列”的按鍵組合,轉義序列將在第3章做更詳細的討論。
2.1.7 C++原始程式碼的格式化
有些語言(如FORTRAN)是面向行的,即每條語句占一行。對於這些語言來說,回車的作用是將語句分開。然而,在C++中,分號標示了語句的結尾。因此,在C++中,回車的作用就和空格或定位字元相同。也就是說,在C++中,通常可以在能夠使用回車的地方使用空格,反之亦然。這說明既可以把一條語句放在幾行上,也可以把幾條語句放在同一行上。例如,可以將myfirst.cpp重新格式化為如下所示:
這樣雖然不太好看,但仍然是合法的代碼。必須遵守一些規則,具體地說,在C和C++中,不能把空格、定位字元或回車放在元素(比如名稱)中間,也不能把回車放在字串中間。下面是一個不能這樣做的例子:
然而,C++11新增的原始(raw)字串可包含回車,這將在第4章簡要地討論。
1.原始程式碼中的標記和空白
一行代碼中不可分割的元素叫做標記(token,參見圖2.3)。通常,必須用空格、定位字元或回車將兩個標記分開,空格、定位字元和回車統稱為空白(white space)。有些字元(如括弧和逗號)是不需要用空白分開的標記。下面的一些示例說明了什麼情況下可以使用空白,什麼情況下可以省略:
圖2.3 標記和空白
2.C++原始程式碼風格
雖然C++在格式方面賦予了您很大的自由,但如果遵循合理的風格,程式將更便於閱讀。有效但難看的代碼不會令人滿意。多數程式師都使用程式清單2.1所示的風格,它遵循了下述規則。
- 每條語句占一行。
- 每個函數都有一個開始花括弧和一個結束花括弧,這兩個花括弧各占一行。
- 函數中的語句都相對於花括弧進行縮進。
- 與函數名稱相關的圓括號周圍沒有空白。
前三條規則旨在確保代碼清晰易讀;第四條規則説明區分函數和一些也使用圓括號的C++內置結構(如迴圈)。在涉及其他指導原則時,本書將提醒讀者。
2.2 C++語句
C++程式是一組函數,而每個函數又是一組語句。C++有好幾種語句,下面介紹其中的一些。程式清單2.2提供了兩種新的語句。聲明語句創建變數,設定陳述式給該變數提供一個值。另外,該程式還演示了cout的新功能。
程式清單2.2 carrot.cpp
空行將聲明語句與程式的其他部分分開。這是C常用的方法,但在C++中不那麼常見。下面是該程式的輸出:
2.2.1 聲明語句和變數
電腦是一種精確的、有條理的機器。要將資訊項存儲在電腦中,必須指出資訊的存儲位置和所需的記憶體空間。在C++中,完成這種任務的一種相對簡便的方法,是使用聲明語句來指出存儲類型並提供位置標籤。例如,程式清單2.2中包含這樣一條聲明語句(注意其中的分號):
這條語句提供了兩項資訊:需要的記憶體以及該記憶體單元的名稱。具體地說,這條語句指出程式需要足夠的存儲空間來存儲一個整數,在C++中用int表示整數。編譯器負責分配和標記記憶體的細節。C++可以處理多種類型的資料,而int是最基本的資料類型。它表示整數—沒有小數部分的數位。C++的int類型可以為正,也可以為負,但是大小範圍取決於實現。第3章將詳細介紹int和其他基本類型。
完成的第二項任務是給存儲單元指定名稱。在這裡,該聲明語句指出,此後程式將使用名稱carrots來標識存儲在該記憶體單元中的值。Carrots被稱為變數,因為它的值可以修改。在C++中,所有變數都必須聲明。如果省略了carrots.cpp中的聲明,則當程式試圖使用carrots時,編譯器將指出錯誤。事實上,程式師嘗試省略聲明,可能只是為了看看編譯器的反應。這樣,以後看到這樣的反應時,便知道應檢查是否省略了聲明。
為什麼變數必須聲明?
有些語言(最典型的是BASIC)在使用新名稱時創建新的變數,而不用顯式地進行聲明。這看上去對用戶比較友好,事實上從短期上說確實如此。問題是,如果錯誤地拼寫了變數名,將在不知情的情況下創建一個新的變數。在BASIC中,ss程式師可能編寫如下語句:
由於CastleDank是拼寫錯誤(將r拼成了n),因此所作的修改實際上並沒有修改CastleDark。這種錯誤很難發現,因為它沒有違反BASIC中的任何規則。然而,在C++中,將聲明CastleDark,但不會聲明被錯誤拼寫的CastleDank,因此對應的C++代碼將違反“使用變數前必須聲明它”的規則,因此編譯器將捕獲這種錯誤,發現潛在的問題。
因此,聲明通常指出了要存儲的資料類型和程式對存儲在這裡的資料使用的名稱。在這個例子中,程式將創建一個名為carrots的變數,它可以存儲整數(參見圖2.4)。
圖2.4 變數聲明
程式中的聲明語句叫做定義聲明(defining declaration)語句,簡稱為定義(definition)。這意味著它將導致編譯器為變數分配記憶體空間。在較為複雜的情況下,還可能有引用聲明(reference declaration)。這些聲明命令電腦使用在其他地方定義的變數。通常,聲明不一定是定義,但在這個例子中,聲明是定義。
如果您熟悉C語言或Pascal,就一定熟悉變數聲明。不過C++中的變數聲明也可能讓人小吃一驚。在C和Pascal中,所有的變數聲明通常都位於函數或過程的開始位置,但C++沒有這種限制。實際上,C++通常的做法是,在首次使用變數前聲明它。這樣,就不必在程式中到處查找,以瞭解變數的類型。本章後面將有一個這樣的例子。這種風格也有缺點,它沒有把所有的變數名放在一起,因此無法對函數使用了哪些變數一目了然(C99標準使C聲明規則與C++非常相似)。
提示:
對於聲明變數,C++的做法是盡可能在首次使用變數前聲明它。
2.2.2 設定陳述式
設定陳述式將值賦給存儲單元。例如,下面的語句將整數25賦給變數carrots表示的記憶體單元:
賦值將從右向左進行。首先,88被賦給steinway;然後,steinway的值(現在是88)被賦給baldwin;然後baldwin的值88被賦給yamaha(C++遵循C的愛好,允許外觀奇怪的代碼)。
程式清單2.2中的第二條設定陳述式表明,可以對變數的值進行修改:
設定運算子右邊的運算式carrots – 1是一個算術運算式。電腦將變數carrots的值25減去1,得到24。然後,設定運算子將這個新值存儲到變數carrots對應的記憶體單元中。
2.2.3 cout的新花樣
到目前為止,本章的示例都使用cout來列印字串,程式清單2.2使用cout來列印變數,該變數的值是一個整數:
如上所示,cout可用於數位和字串。這似乎沒有什麼不同尋常的地方,但別忘了,整數25與字串“25”有天壤之別。該字串存儲的是書寫該數位時使用的字元,即字元3和8。程式在內部存儲的是字元3和字元8的編碼。要列印字串,cout只需列印字串中各個字元即可。但整數25被存儲為數值,電腦不是單獨存儲每個數位,而是將25存儲為二進位數字(附錄A討論了這種標記法)。這裡的要點是,在列印之前,cout必須將整數形式的數位轉換為字串形式。另外,cout很聰明,知道carrots是一個需要轉換的整數。
與老式C語言的區別在於cout的聰明程度。在C語言中,要列印字串“25”和整數25,可以使用C語言的多功能輸出函數printf( ):
撇開printf( )的複雜性不說,必須用特殊代碼(%s和%d)來指出是要列印字串還是整數。如果讓printf( )列印字串,但又錯誤地提供了一個整數,由於printf( )不夠精密,因此根本發現不了錯誤。它將繼續處理,顯示一堆亂碼。
cout的智慧行為源自C++的物件導向特性。實際上,C++插入運算子(<<)將根據其後的資料類型相應地調整其行為,這是一個運算子重載的例子。在後面的章節中學習函數重載和運算子重載時,將知道如何實現這種智慧設計。
cout和printf( )
如果已經習慣了C語言和printf( ),可能覺得cout看起來很奇怪。程式師甚至可能固執地堅持使用printf( )。但與使用所有轉換說明的printf( )相比,cout的外觀一點也不奇怪。更重要的是,cout還有明顯的優點。它能夠識別類型的功能表明,其設計更靈活、更好用。另外,它是可擴展的(extensible)。也就是說,可以重新定義<<運算子,使cout能夠識別和顯示所開發的新資料類型。如果喜歡printf( )提供的細緻的控制功能,可以使用更高級的cout來獲得相同的效果(參見第17章)。
2.3 其他C++語句
再來看幾個C++語句的例子。程式清單2.3中的程式對前一個程式進行了擴展,要求在程式運行時輸入一個值。為實現這項任務,它使用了cin,這是與cout對應的用於輸入的物件。另外,該程式還演示了cout物件的多功能性。
程式清單2.3 getinfo.cpp
程式調整
如果您發現在以前的程式清單中需要添加cin.get( ),則在這個程式清單中,需要添加兩條cin.get( )語句,這樣才能在螢幕上看到輸出。第一條cin.get( )語句在您輸入數位並按Enter鍵時讀取輸入,而第二條cin.get( )語句讓程式暫停,直到您按Enter鍵。
下面是該程式的運行情況:
該套裝程式含兩項新特性:用cin來讀取鍵盤輸入以及將四條輸出語句組合成一條。下面分別介紹它們。
2.3.1 使用cin
上面的輸出表明,從鍵盤輸入的值(12)最終被賦給變數carrots。下面就是執行這項功能的語句:
從這條語句可知,資訊從cin流向carrots。顯然,對這一過程有更為正式的描述。就像C++將輸出看作是流出程式的字元流一樣,它也將輸入看作是流入程式的字元流。iostream檔將cin定義為一個表示這種流的物件。輸出時,<<運算子將字串插入到輸出流中;輸入時,cin使用>>運算子從輸入流中抽取字元。通常,需要在運算子右側提供一個變數,以接收抽取的資訊(符號<<和>>被選擇用來指示資訊流的方向)。
與cout一樣,cin也是一個智慧物件。它可以將通過鍵盤輸入的一系列字元(即輸入)轉換為接收資訊的變數能夠接受的形式。在這個例子中,程式將carrots聲明為一個整型變數,因此輸入被轉換為電腦用來存儲整數的數位形式。
2.3.2 使用cout進行拼接
getinfo.cpp中的另一項新特性是將4條輸出語句合併為一條。iostream檔定義了<<運算子,以便可以像下面這樣合併(拼接)輸出:
這樣能夠將字串輸出和整數輸出合併為一條語句。得到的輸出與下述代碼生成的相似:
根據有關cout的建議,也可以按照這樣的方式重寫拼接版本,即將一條語句放在4行上:
這是由於C++的自由格式規則將標記間的分行符號和空格看作是可相互替換的。當代碼行很長,限制輸出的顯示風格時,最後一種技術很方便。
需要注意的另一點是:
和
在同一行中。
這是因為前面指出過的,cout語句的輸出緊跟在前一條cout語句的輸出後面。即使兩條cout語句之前有其他語句,情況也將如此。
2.3.3 類簡介
看了足夠多的cin和cout示例後,可以學習有關物件的知識了。具體地說,本節將進一步介紹有關類的知識。正如第1章指出的,類是C++中物件導向程式設計(OOP)的核心概念之一。
類是用戶定義的一種資料類型。要定義類,需要描述它能夠表示什麼資訊和可對資料執行哪些操作。類之於物件就像類型之於變數。也就是說,類定義描述的是資料格式及其用法,而物件則是根據資料格式規範創建的實體。換句話說,如果說類就好比所有著名演員,則物件就好比某個著名的演員,如蛙人Kermit。我們來擴展這種類比,表示演員的類中包括該類可執行的操作的定義,如念某一角色的臺詞,表達悲傷、威脅恫嚇,接受獎勵等。如果瞭解其他OOP術語,就知道C++類對應於某些語言中的物件類型,而C++物件對應於物件實例或執行個體變數。
下面更具體一些。前文講述過下面的變數聲明:
上面的代碼將創建一個類型為int的變數(carrots)。也就是說,carrots可以存儲整數,可以按特定的方式使用—例如,用於加和減。現在來看cout。它是一個ostream類物件。ostream類定義(iostream檔的另一個成員)描述了ostream物件表示的資料以及可以對它執行的操作,如將數位或字串插入到輸出流中。同樣,cin是一個istream類物件,也是在iostream中定義的。
注意:
類描述了一種資料類型的全部屬性(包括可使用它執行的操作),物件是根據這些描述創建的實體。
知道類是用戶定義的類型,但作為用戶,並沒有設計ostream和istream類。就像函數可以來自函式程式庫一樣,類也可以來自類庫。ostream和istream類就屬於這種情況。從技術上說,它們沒有被內置到C++語言中,而是語言標準指定的類。這些類定義位於iostream檔中,沒有被內置到編譯器中。如果願意,程式師甚至可以修改這些類定義,雖然這不是一個好主意(準確地說,這個主意很糟)。iostream系列類和相關的fstream(或檔I/O)系列類是早期所有的實現都自帶的唯一兩組類定義。然而,ANSI/ISO C++委員會在C++標準中添加了其他一些類庫。另外,多數實現都在套裝軟體中提供了其他類定義。事實上,C++當前之所以如此有吸引力,很大程度上是由於存在大量支持UNIX、Macintosh和Windows程式設計的類庫。
類描述指定了可對類物件執行的所有操作。要對特定物件執行這些允許的操作,需要給該物件發送一條消息。例如,如果希望cout物件顯示一個字串,應向它發送一條消息,告訴它,“物件!顯示這些內容!”C++提供了兩種發送消息的方式:一種方式是使用類方法(本質上就是稍後將介紹的函式呼叫);另一種方式是重新定義運算子,cin和cout採用的就是這種方式。因此,下面的語句使用重新定義的<<運算子將“顯示消息”發送給cout:
在這個例子中,消息帶一個參數—要顯示的字串(參見圖2.5)。
圖2.5 向物件發送消息
2.4 函數
由於函數用於創建C++程式的模組,對C++的OOP定義至關重要,因此必須熟悉它。函數的某些方面屬於高級主題,將在第7章和第8章重點討論函數。然而,現在瞭解函數的一些基本特徵,將使得在以後的函數學習中更加得心應手。本章剩餘的內容將介紹函數的一些基本知識。
C++函數分兩種:有返回值的和沒有返回值的。在標準C++函式程式庫中可以找到這兩類函數的例子,您也可以自己創建這兩種類型的函數。下面首先來看一個有返回值的庫函數,然後介紹如何編寫簡單的函數。
2.4.1 使用有返回值的函數
有返回值的函數將生成一個值,而這個值可賦給變數或在其他運算式中使用。例如,標準C/C++庫包含一個名為sqrt( )的函數,它返回平方根。假設要計算6.25的平方根,並將這個值賦給變數x,則可以在程式中使用下面的語句:
運算式sqrt(6.25)將調用sqrt( )函數。運算式sqrt(6.25)被稱為函式呼叫,被調用的函數叫做被調用函數(called function),包含函式呼叫的函數叫做調用函數(calling function,參見圖2.6)。
圓括號中的值(這裡為6.25)是發送給函數的資訊,這被稱為傳遞給函數。以這種方式發送給函數的值叫做參數。(參見圖2.7。)函數sqrt( )得到的結果為2.5,並將這個值發送給調用函數;發送回去的值叫做函數的返回值(return value)。可以這麼認為,函數執行完畢後,語句中的函式呼叫部分將被替換為返回的值。因此,這個例子將返回值賦給變數x。簡而言之,參數是發送給函數的資訊,返回值是從函數中發送回去的值。
圖2.6 調用函數
圖2.7 函式呼叫的句法
情況基本上就是這樣,只是在使用函數之前,C++編譯器必須知道函數的參數類型和返回數值型別。也就是說,函數是返回整數、字元、小數、有罪裁決還是別的什麼東西?如果缺少這些資訊,編譯器將不知道如何解釋返回值。C++提供這種資訊的方式是使用函數原型語句。
注意:
C++程式應當為程式中使用的每個函數提供原型。
函數原型之於函數就像變數聲明之於變數—指出涉及的類型。例如,C++庫將sqrt( )函式定義成將一個(可能)帶小數部分的數位(如6.25)作為參數,並返回一個相同類型的數字。有些語言將這種數位稱為實數,但是C++將這種類型稱為double(將在第3章介紹)。sqrt( )的函數原型像這樣:
第一個double意味著sqrt( )將返回一個double值。括弧中的double意味著sqrt( )需要一個double參數。因此該原型對sqrt( )的描述和下面代碼中使用的函數相同:
原型結尾的分號表明它是一條語句,這使得它是一個原型,而不是函數頭。如果省略分號,編譯器將把這行代碼解釋為一個函數頭,並要求接著提供定義該函數的函數體。
在程式中使用sqrt( )時,也必須提供原型。可以用兩種方法來實現:
- 在原始程式碼檔中輸入函數原型;
- 包含標頭檔cmath(老系統為math.h),其中定義了原型。
第二種方法更好,因為標頭檔更有可能使原型正確。對於C++庫中的每個函數,都在一個或多個頭檔中提供了其原型。請通過手冊或線上說明查看函數描述來確定應使用哪個標頭檔。例如,sqrt( )函數的說明將指出,應使用cmath標頭檔。(同樣,可能必須使用老式的標頭檔math.h,它可用於C和C++程式中。)
不要混淆函數原型和函式定義。可以看出,原型只描述函數介面。也就是說,它描述的是發送給函數的資訊和返回的資訊。而定義中包含了函數的代碼,如計算平方根的代碼。C和C++將庫函數的這兩項特性(原型和定義)分開了。庫檔中包含了函數的編譯代碼,而標頭檔中則包含了原型。
應在首次使用函數之前提供其原型。通常的做法是把原型放到main( )函式定義的前面。程式清單2.4演示了庫函數sqrt( )的用法,它通過包含cmath檔來提供該函數的原型:
程式清單2.4 sqrt.cpp
注意:
如果使用的是老式編譯器,則必須在程式清單2.4中使用#include <math.h>,而不是#include<cmath>。
使用庫函數
C++庫函數存儲在庫檔中。編譯器編譯器時,它必須在庫檔搜索您使用的函數。至於自動搜索哪些庫檔,將因編譯器而異。如果運行程式清單2.4時,將得到一條消息,指出_sqrt是一個沒有定義的外部函數(似乎應當避免),則很可能是由於編譯器不能自動搜索數學庫(編譯器傾向於給函數名添加底線首碼—提示它們對程式具有最後的發言權)。如果在UNIX實現中遇到這樣的消息,可能需要在命令列結尾使用-lm選項:在Linux系統中,有些版本的Gnu編譯器與此類似:只包含cmath標頭檔可以提供原型,但不一定會導致編譯器搜索正確的庫檔。
下面是該程式的運行情況:
由於sqrt( )處理的是double值,因此這裡將變數聲明為這種類型。聲明double變數的句法與聲明int變數相同:
double類型使得變數area和side能夠存儲帶小數的值,如1 536.0和39.191 8。將看起來是整數(如1536)的值賦給double變數時,將以實數形式存儲它,其中的小數部分為.0。在第3章將指出,double類型覆蓋的範圍要比int類型大得多。
C++允許在程式的任何地方聲明新變數,因此sqrt.cpp在要使用side時才聲明它。C++還允許在創建變數時對它進行賦值,因此也可以這樣做:
這個過程叫做初始化(initialization),將在第3章更詳細地介紹。
cin知道如何將輸入流中的資訊轉換為double類型,cout知道如何將double類型插入到輸出流中。前面講過,這些物件都很智慧化。
2.4.2 函數變體
有些函數需要多項資訊。這些函數使用多個參數,參數間用逗號分開。例如,數學函數pow( )接受兩個參數,返回值為以第一個參數為底,第二個參數為指數的冪。該函數的原型如下:
要計算5的8次方,可以這樣使用該函數:
另外一些函數不接受任何參數。例如,有一個C庫(與cstdlib或stdlib.h標頭檔相關的庫)包含一個rand( )函數,該函數不接受任何參數,並返回一個隨機整數。該函數的原型如下:
關鍵字void明確指出,該函數不接受任何參數。如果省略void,讓括弧為空,則C++將其解釋為一個不接受任何參數的隱式聲明。可以這樣使用該函數:
注意,與其他一些電腦語言不同,在C++中,函式呼叫中必須包括括弧,即使沒有參數。
還有一些函數沒有返回值。例如,假設編寫了一個函數,它按美元、美分格式顯示數位。當向它傳遞參數23.5時,它將在螢幕上顯示$23.50。由於這個函數把值發送給螢幕,而不是調用程式,因此不需要返回值。可以在原型中使用關鍵字void來指定返回類型,以指出函數沒有返回值:
由於它不返回值,因此不能將該函式呼叫放在設定陳述式或其他運算式中。相反,應使用一條純粹的函式呼叫語句:
在有些語言中,有返回值的函數被稱為函數(function);沒有返回值的函數被稱為過程(procedure)或副程式(subroutine)。但C++與C一樣,這兩種變體都被稱為函數。
2.4.3 使用者定義的函數
標準C庫提供了140多個預定義的函數。如果其中的函數能滿足要求,則應使用它們。但使用者經常需要編寫自己的函數,尤其是在設計類的時候。無論如何,設計自己的函數很有意思,下面來介紹這一過程。前面已經使用過好幾個使用者定義的函數,它們都叫main( )。每個C++程式都必須有一個main( )函數,使用者必須對它進行定義。假設需要添加另一個使用者定義的函數。和庫函數一樣,也可以通過函數名來調用使用者定義的函數。對於庫函數,在使用之前必須提供其原型,通常把原型放到main( )定義之前。但現在您必須提供新函數的原始程式碼。最簡單的方法是,將代碼放在main( )的後面。程式清單2.5演示了這些元素。
程式清單2.5 ourfunc.cpp
main( )函數兩次調用simon( )函數,一次的參數為3,另一次的參數為變數count。在這兩次調用之間,使用者輸入一個整數,用來設置count的值。這個例子沒有在cout提示消息中使用分行符號。這樣將導致用戶輸入與提示出現在同一行中。下面是運行情況:
1.函數格式
在程式清單2.5中,simon( )函數的定義與main( )的定義採用的格式相同。首先,有一個函數頭;然後是花括弧中的函數體。可以把函數的格式統一為如下的情形:
注意,定義simon( )的原始程式碼位於main( )的後面。和C一樣(但不同於Pascal),C++不允許將函式定義嵌套在另一個函式定義中。每個函式定義都是獨立的,所有函數的創建都是平等的(參見圖2.8)。
2.函數頭
在程式清單2.5中,simon( )函數的函數頭如下:
開頭的void表明simon( )沒有返回值,因此調用simon( )不會生成可在main( )中將其賦給變數的數位。因此,第一個函式呼叫方式如下:
由於simon( )沒有返回值,因此不能這樣使用它:
圖2.8 函式定義在檔中依次出現
括弧中的int n表明,使用simon( )時,應提供一個int參數。n是一個新的變數,函式呼叫時傳遞的值將被賦給它。因此,下面的函式呼叫將3賦給simon( )函數頭中定義的變數n:
當函數體中的cout語句使用n時,將使用函式呼叫時傳遞的值。這就是為什麼simon(3)在輸出中顯示3的原因所在。在示例運行中,函式呼叫simon(count)導致函數顯示512,因為這正是賦給count的值。簡而言之,simon( )的函數頭表明,該函數接受一個int參數,不返回任何值。
下面複習一下main( )的函數頭:
開頭的int表明,main( )返回一個整數值;空括弧(其中可以包含void)表明,main( )沒有參數。對於有返回值的函數,應使用關鍵字return來提供返回值,並結束函數。這就是為什麼要在main( )結尾使用下述語句的原因:
答案是,可以將電腦作業系統(如UNIX或Windows)看作調用程式。因此,main( )的返回值並不是返回給程式的其他部分,而是返回給作業系統。很多作業系統都可以使用程式的返回值。例如,UNIX外殼腳本和Windows命令列批次檔都被設計成運行程式,並測試它們的返回值(通常叫做退出值)。通常的約定是,退出值為0則意味著程式運行成功,為非零則意味著存在問題。因此,如果C++程式無法打開檔,可以將它設計為返回一個非零值。然後,便可以設計一個外殼腳本或批次檔來運行該程式,如果該程式發出指示失敗的消息,則採取其他措施。
關鍵字
關鍵字是電腦語言中的詞彙。本章使用了4個C++關鍵字:int、void、return和double。由於這些關鍵字都是C++專用的,因此不能用作他用。也就是說,不能將return用作變數名,也不能把double用作函數名。不過可以把它們用作名稱的一部分,如painter(其中包含int)或return_aces。附錄B提供了C++關鍵字的完整列表。另外,main不是關鍵字,由於它不是語言的組成部分。然而,它是一個必不可少的函數的名稱。可以把main用作變數名(在一些很神秘的以致於無法在這裡介紹的情況中,將main用作變數名會引發錯誤,由於它在任何情況下都是容易混淆的,因此最好不要這樣做)。同樣,其他函數名和物件名也都不能是關鍵字。然而,在程式中將同一個名稱(比如cout)用作物件名和變數名會把編譯器搞糊塗。也就是說,在不使用cout物件進行輸出的函數中,可以將cout用作變數名,但不能在同一個函數中同時將cout用作物件名和變數名。
2.4.4 用戶定義的有返回值的函數
我們再深入一步,編寫一個使用返回語句的函數。main( )函數已經揭示了有返回值的函數的格式:在函數頭中指出返回類型,在函數體結尾處使用return。可以用這種形式為在英國觀光的人解決重量的問題。在英國,很多浴室都以英石(stone)為單位,不像美國以磅或公斤為單位。一英石等於14磅,程式清單2.6使用一個函數來完成這樣的轉換。
程式清單2.6 convert.cpp
下面是該程式的運行情況:
在main( )中,程式使用cin來給整型變數stone提供一個值。這個值被作為參數傳遞給stonetolb( )函數,在該函數中,這個值被賦給變數sts。然後,stonetolb( )用關鍵字return將14*sts返回給main( )。這表明return後面並非一定得跟一個簡單的數字。這裡通過使用較為複雜的運算式,避免了創建一個新變數,將結果賦給該變數,然後將它返回。程式將計算運算式的值(這裡為210),並將其返回。如果返回運算式的值很麻煩,可以採取更複雜的方式:
這兩個版本返回的結果相同,但第二個版本更容易理解和修改,因為它將計算和返回分開了。
通常,在可以使用一個簡單常量的地方,都可以使用一個返回數值型別與該常量相同的函數。例如,stonetolb( )返回一個int值,這意味著可以以下面的方式使用該函數:
在上述任何一種情況下,程式都將計算返回值,然後在語句中使用這個值。
這些例子表明,函數原型描述了函數介面,即函數如何與程式的其他部分交互。參數清單指出了何種資訊將被傳遞給函數,函數類型指出了返回值的類型。程式師有時將函數比作一個由出入它們的資訊所指定的黑盒子(black boxes)(電工用語)。函數原型將這種觀點詮釋得淋漓盡致(參見圖2.9)。
圖2.9 函數原型和作為黑盒的函數
函數stonetolb( )短小、簡單,但包含了全部的函數特性:
- 有函數頭和函數體;
- 接受一個參數;
- 返回一個值;
- 需要一個原型。
可以把stonetolb( )看作函數設計的標準格式。第7章和第8章將更詳細地介紹函數。而本章的內容讓讀者能夠很好地瞭解函數的工作方式及其如何與C++匹配。
2.4.5 在多函數程式中使用using編譯指令
在程式清單2.5中,兩個函數中都包含下面一條using編譯指令:
這是因為每個函數都使用了cout,因此需要能夠訪問位於名稱空間std中的cout定義。
在程式清單2.5中,可以採用另一種方法讓兩個函數都能夠訪問名稱空間std,即將編譯指令放在函數的外面,且位於兩個函數的前面:
當前通行的理念是,只讓需要訪問名稱空間std的函數訪問它是更好的選擇。例如,在程式清單2.6中,只有main( )函數使用cout,因此沒有必要讓函數stonetolb( )能夠訪問名稱空間std。因此編譯指令using被放在函數main( )中,使得只有該函數能夠訪問名稱空間std。
總之,讓程式能夠訪問名稱空間std的方法有多種,下面是其中的4種。
- 將using namespace std;放在函式定義之前,讓檔中所有的函數都能夠使用名稱空間std中所有的元素。
- 將using namespace std;放在特定的函式定義中,讓該函數能夠使用名稱空間std中的所有元素。
- 在特定的函數中使用類似using std::cout;這樣的編譯指令,而不是using namespace std;,讓該函數能夠使用指定的元素,如cout。
- 完全不使用編譯指令using,而在需要使用名稱空間std中的元素時,使用首碼std::,如下所示:
命名約定
C++程式師給函數、類和變數命名時,可以有很多種選擇。程式師對風格的觀點五花八門,這些看法有時就像公共論壇上的聖戰。就函數名稱而言,程式師有以下選擇:Myfunction( ) myfunction( ) myFunction( ) my_function( ) my_funct( )選擇取決於開發團體、使用的技術或庫以及程式師個人的品位和喜好。因此凡是符合第3章將介紹的C++規則的風格都是正確的,都可以根據個人的判斷而使用。撇開語言是否允許不談,個人的命名風格也是值得注意的—它有助於保持一致性和精確性。精確、讓人一目了然的個人命名約定是良好的軟體工程的標誌,它在整個程式設計生涯中都會起到很好的作用。
2.5 總結
C++程式由一個或多個被稱為函數的模組組成。程式從main( )函數(全部小寫)開始執行,因此該函數必不可少。函數由函數頭和函數體組成。函數頭指出函數的返回值(如果有的話)的類型和函數期望通過參數傳遞給它的資訊的類型。函數體由一系列位於花括弧({})中的C++語句組成。
有多種類型的C++語句,包括下述6種。
- 聲明語句:定義函數中使用的變數的名稱和類型。
- 設定陳述式:使用設定運算子(=)給變數賦值。
- 消息語句:將消息發送給物件,激發某種行動。
- 函式呼叫:執行函數。被調用的函數執行完畢後,程式返回到函式呼叫語句後面的語句。
- 函數原型:聲明函數的返回類型、函數接受的參數數量和類型。
- 返回語句:將一個值從被調用的函數那裡返回到調用函數中。
類是使用者定義的資料類型規範,它詳細描述了如何表示資訊以及可對資料執行的操作。物件是根據類規範創建的實體,就像簡單變數是根據資料類型描述創建的實體一樣。
C++提供了兩個用於處理輸入和輸出的預定義物件(cin和cout),它們是istream和ostream類的實例,這兩個類是在iostream檔中定義的。為ostream類定義的插入運算子(<<)使得將資料插入到輸出流成為可能;為istream類定義的抽取運算子(>>)能夠從輸入流中抽取資訊。cin和cout都是智慧物件,能夠根據程式上下文自動將資訊從一種形式轉換為另一種形式。
C++可以使用大量的C庫函數。要使用庫函數,應當包含提供該函數原型的標頭檔。
至此,讀者對簡單的C++程式有了大致的瞭解,可以進入下一章,瞭解程式的細節。
2.6 複習題
在附錄J中可以找到所有複習題的答案。
1.C++程式的模組叫什麼?
2.下面的前置處理器編譯指令是做什麼用的?
3.下面的語句是做什麼用的?
5.什麼語句可以用來創建名為cheeses的整數變數?
6.什麼語句可以用來將值32賦給變數cheeses?
7.什麼語句可以用來將從鍵盤輸入的值讀入變數cheeses中?
8.什麼語句可以用來列印“We have X varieties of cheese,”,其中X為變數cheeses的當前值。
9.下面的函數原型指出了關於函數的哪些資訊?
10.定義函數時,在什麼情況下不必使用關鍵字return?
11.假設您編寫的main( )函數包含如下代碼:
而編譯器指出cout是一個未知識別字。導致這種問題的原因很可能是什麼?指出3種修復這種問題的方法。
2.7 程式設計練習
1.編寫一個C++程式,它顯示您的姓名和位址。
2.編寫一個C++程式,它要求使用者輸入一個以long為單位的距離,然後將它轉換為碼(一long等於220碼)。
3.編寫一個C++程式,它使用3個使用者定義的函數(包括main( )),並生成下面的輸出:
其中一個函數要調用兩次,該函數生成前兩行;另一個函數也被調用兩次,並生成其餘的輸出。
5.編寫一個程式,其中的main( )調用一個使用者定義的函數(以攝氏溫度值為參數,並返回相應的華氏溫度值)。該程式按下面的格式要求使用者輸入攝氏溫度值,並顯示結果:
下面是轉換公式:
華氏溫度 = 1.8×攝氏溫度 + 32.0
6.編寫一個程式,其main( )調用一個使用者定義的函數(以光年值為參數,並返回對應天文單位的值)。該程式按下面的格式要求用戶輸入光年值,並顯示結果:
天文單位是從地球到太陽的平均距離(約150000000公里或93000000英里),光年是光一年走的距離(約10萬億公里或6萬億英里)(除太陽外,最近的恒星大約離地球4.2光年)。請使用double類型(參見程式清單2.4),轉換公式為:
1光年=63240天文單位
7.編寫一個程式,要求使用者輸入小時數和分鐘數。在main( )函數中,將這兩個值傳遞給一個void函數,後者以下面這樣的格式顯示這兩個值:















































































0 留言:
發佈留言