這本銷量50萬+的經典書C++ Primer Plus終於有了習題解答

程序員書屋 發佈 2020-06-29T08:38:12+00:00

在20世紀90年代便是最重要的程式語言之一,並在21世紀仍保持強勁勢頭。C++繼承了C語言高效、簡潔、快速和可移植性的傳統。C++面向對象的特性帶來了全新的編程方法,這種方法是為應付複雜程度不斷提高的現代編程任務而設計的。C++的模板特性提供了另一種全新的編程方法——泛型編程。

在20世紀90年代便是最重要的程式語言之一,並在21世紀仍保持強勁勢頭。C++繼承了C語言高效、簡潔、快速和可移植性的傳統。C++面向對象的特性帶來了全新的編程方法,這種方法是為應付複雜程度不斷提高的現代編程任務而設計的。C++的模板特性提供了另一種全新的編程方法——泛型編程。這三件法寶既是福也是禍,一方面讓C++語言功能強大,另一方面則意味著有更多的東西需要學習。

C++融合了3種不同的編程方式:C語言代表的過程性語言、C++在C語言基礎上添加的類代表的面向對象語言、C++模板支持的泛型編程。使用C++的原因之一是為了利用其面向對象的特性。要利用這種特性,必須對標準C語言知識有較深入的了解,因為它提供了基本類型、運算符、控制結構和語法規則。所以,如果已經對C有所了解,便可以學習C++了,但這並不僅僅是學習更多的關鍵字和結構,從C過渡到C++的學習量就像從頭學習C語言一樣大。另外,如果先掌握了C語言,則在過渡到C++時,必須摒棄一些編程習慣。如果不了解C語言,則學習C++時需要掌握C語言的知識、OOP知識以及泛型編程知識,但無需摒棄任何編程習慣。如果您認為學習C++可能需要擴展思維,這就對了。

如果你想學習C++,選擇 C++ Primer Plus 第6版 中文版准沒錯。

C++ Primer Plus 第6版 中文版在今年重新再版。

劃重點:

C++教程十年新版再現,經久不衰的C++暢銷經典教程,中文版累計銷量超500000冊,2020版贈送價值99元e讀版電子書及在線實驗環境,附贈大尺寸(60CM*80CM)全書學習思維導圖。

  • 全新升級,針對C++11標準全面更新;
  • 專為零基礎讀者撰寫,近百萬程式設計師的C++編程啟蒙教程;
  • 示意圖解釋概念,方面理解;
  • 隨時指出潛存的問題,避免走彎路;
  • 隨處可見的警告、注意、提示隨時給讀者以警醒;
  • 庖丁解牛式分析程序,確保知其然更知其所以然;
  • 精心設計複習題、編程練習,檢驗學習中的問題,提示就業面試筆試的要點;
  • 登陸異步社區,免費獲得本書配套原始碼等資源。

這本書在豆瓣:

豆瓣評分8.6,下圖是來自豆瓣的一段中肯建議:

本書遵循的C++標準

當代的編譯器都對C++98提供了很好的支持。編寫本書期間,有些編譯器還支持一些C++11 特性;隨著新標準獲批,對這些特性的支持將很快得到提高。本書反映了當前的情形,詳盡地介紹了C++98,並涵蓋了C++11新增的一些特性。在探討相關的C++98主題時順便介紹了一些C++新特性,而第18章專門介紹新特性,它總結了本書前面提到的一些特性,並介紹了其他特性。

在編寫本書期間,對C++11的支持還不全面,因此難以全面介紹C++11新增的所有特性。考慮到篇幅限制,即使這個新標準獲得了全面支持,也無法在一本書中全面介紹它。本書重點介紹大多數編譯器都支持的特性,並簡要地總結其他特性。

本文重點C++ Primer Plus 第6版 中文版習題解答來啦!


《C++ Primer Plus(第6版)中文版習題解答》是超級暢銷書《C++ Primer Plus(第6版)中文版》的配套習題答案,針對書中的複習題和編程練習,給出了解題思路和答案。

《C++ Primer Plus(第6版)中文版習題解答》共分為18章,每一章的主題與《C++ Primer Plus(第6版)中文版》完全一致。每章開篇採用思維導圖的方式列出本章的知識點,然後對每章的重點內容進行了梳理總結,最後則對每章中的複習題和編程練習進行了分析並給出了解答思路,確保讀者在徹底夯實理論知識的同時,進一步提升實際編程能力。

作為《C++ Primer Plus(第6版)中文版》的配套參考書,《C++ Primer Plus(第6版)中文版習題解答》特別適合需要系統學習C++語言的初學者閱讀,也適合打算鞏固C++語言知識或者希望進一步提高編程技術的程式設計師閱讀。

關於作者:
Stephen Prata曾在加利福尼亞的馬林學院(肯特菲爾德)教授天文學、物理學和程序設計課程,現已退休。他在加州理工學院獲得學士學位,在加州大學伯克利分校獲得博士學位。他最早接觸程序設計,是為了利用計算機給星團建模。Stephen撰寫和與他人合著了十幾本書籍,其中包括C Primer Plus和Unix Primer Plus。



截選本書第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

// myfirst.cpp -- displays a message

#include <iostream>                            // a PREPROCESSOR directive
int main()                                     // function header
{                                              // start of function body
    using namespace std;                       // make definitions visible
    cout << "Come up and C++ me some time.";   // message
    cout << endl;                              // start a new line
    cout << "You won』t regret it!" << endl;    // more output
    return 0; // terminate main()
}

程序調整

要在自己的系統上運行本書的示例,可能需要對其進行修改。有些窗口環境在獨立的窗口中運行程序,並在程序運行完畢後自動關閉該窗口。正如第1章討論的,要讓窗口一直打開,直到您按任何鍵,可在return語句前添加如下語句:

cin.get();

對於有些程序,要讓窗口一直打開,直到您按任何鍵,必須添加兩條這樣的語句。第4章將更詳細地介紹cin.get()。

如果您使用的系統很舊,它可能不支持C++98新增的特性。

有些程序要求編譯器對C++11標準提供一定的支持。對於這樣的程序,將明確的指出這一點,並在可能的情況下提供非C++11代碼。


將該程序複製到您選擇的編輯器中(或使用本書配套網站的原始碼,詳情請參閱封底)後,便可以C++編譯器創建可執行代碼了(參見第1章的介紹)。下面是運行編譯後的程序時得到的輸出:

Come up and C++ me some time.
You won』t regret it!

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中的示例程序的基本結構如下:

int main()
{
    statements
    return 0;
}

這幾行表明有一個名為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()函數可以給調用它的函數返回一個整數值,且不從調用它的函數那裡獲得任何信息:

int main()

很多現有的程序都使用經典C函數頭:

main()        // original C style

在C語言中,省略返回類型相當於說函數的類型為int。然而,C++逐步淘汰了這種用法。

也可以使用下面的變體:

int main(void)       // very explicit style

在括號中使用關鍵字void明確地指出,函數不接受任何參數。在C++(不是C)中,讓括號空著與在括號中使用void等效(在C中,讓括號空著意味著對是否接受參數保持沉默)。

有些程式設計師使用下面的函數頭,並省略返回語句:

void main()

這在邏輯上是一致的,因為void返回類型意味著函數不返回任何值。該變體適用於很多系統,但由於它不是當前標準強制的一個選項,因此在有些系統上不能工作。因此,讀者應避免使用這種格式,而應使用C++標準格式,這不需要做太多的工作就能完成。

最後,ANSI/ISO C++標準對那些抱怨必須在main()函數最後包含一條返回語句過於繁瑣的人做出了讓步。如果編譯器到達main()函數末尾時沒有遇到返回語句,則認為main()函數以如下語句結尾:

return 0;

這條隱含的返回語句只適用於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就像沒有注釋一樣:

#include <iostream>
int main()
{
    using namespace std;
    cout << "Come up and C++ me some time.";
    cout << endl;
    cout << "You won』t regret it!" << endl;
    return 0;
}

C++注釋以//打頭,到行尾結束。注釋可以位於單獨的一行上,也可以和代碼位於同一行。請注意程序清單2.1的第一行:

// myfirst.cpp -- displays a message

本書所有的程序都以注釋開始,這些注釋指出了原始碼的文件名並簡要地總結了該程序。在第1章中介紹過,原始碼的文件擴展名取決於所用的C++系統。在其他系統中,文件名可能為myfirst.C或myfirst.cxx。

提示: 

應使用注釋來說明程序。程序越複雜,注釋的價值越大。注釋不僅有助於他人理解這些代碼,也有助於程式設計師自己理解代碼,特別是隔了一段時間沒有接觸該程序的情況下。




C-風格注釋

C++也能夠識別C注釋,C注釋包括在符號/和/之間:

#include <iostream> /* a C-style comment */

由於C-風格注釋以*/結束,而不是到行尾結束,因此可以跨越多行。可以在程序中使用C或C++風格的注釋,也可以同時使用這兩種注釋。但應儘量使用C++注釋,因為這不涉及到結尾符號與起始符號的正確配對,所以它產生問題的可能性很小。事實上,C++標準也在C語言中添加了//注釋。



2.1.3 C++預處理器和iostream文件

下面簡要介紹一下需要知道的一些知識。如果程序要使用C++輸入或輸出工具,請提供這樣兩行代碼:

#include <iostream>
using namespace std;

可使用其他代碼替換第2行,這裡使用這行代碼旨在簡化該程序(如果編譯器不接受這幾行代碼,則說明它沒有遵守標準C++98,使用它來編譯本書的示例時,將出現眾多其他的問題)。為使程序正常工作,只需要知道這些。下面更深入地介紹一下這些內容。

C++和C一樣,也使用一個預處理器,該程序在進行主編譯之前對源文件進行處理(第1章介紹過,有些C++實現使用翻譯器程序將C++程序轉換為C程序。雖然翻譯器也是一種預處理器,但這裡不討論這種預處理器,而只討論這樣的預處理器,即它處理名稱以#開頭的編譯指令)。不必執行任何特殊的操作來調用該預處理器,它會在編譯程序時自動運行。

程序清單2.1使用了#include編譯指令:

#include <iostream>     // a PREPROCESSOR directive

該編譯指令導致預處理器將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對頭文件的命名約定進行了總結。

由於C使用不同的文件擴展名來表示不同文件類型,因此用一些特殊的擴展名(如.hpp或.hxx)表示C++頭文件是有道理的,ANSI/ISO委員會也這樣認為。問題在於究竟使用哪種擴展名,因此最終他們一致同意不使用任何擴展名。

2.1.5 名稱空間

如果使用iostream,而不是iostream.h,則應使用下面的名稱空間編譯指令來使iostream中的定義對程序可用:

using namespace std;

這叫作using編譯指令。最簡單的辦法是,現在接受這個編譯指令,以後再考慮它(例如,到第9章再考慮它)。但這裡還是簡要地介紹它,以免您一頭霧水。

名稱空間支持是一項C++特性,旨在讓您編寫大型程序以及將多個廠商現有的代碼組合起來的程序時更容易,它還有助於組織程序。一個潛在的問題是,可能使用兩個已封裝好的產品,而它們都包含一個名為wanda()的函數。這樣,使用wanda()函數時,編譯器將不知道指的是哪個版本。名稱空間讓廠商能夠將其產品封裝在一個叫作名稱空間的單元中,這樣就可以用名稱空間的名稱來指出想使用哪個廠商的產品。因此,Microflop Industries可以將其定義放到一個名為Microflop的名稱空間中。這樣,其wanda()函數的全稱為Microflop::wanda();同樣,Piscine公司的wanda()版本可以表示為Piscine::wanda()。這樣,程序就可以使用名稱空間來區分不同的版本了:

Microflop::wanda("go dancing?");       // use Microflop namespace version
Piscine::wanda("a fish named Desire"); // use Piscine namespace version

按照這種方式,類、函數和變量便是C++編譯器的標準組件,它們現在都被放置在名稱空間std中。僅當頭文件沒有擴展名h時,情況才是如此。這意味著在iostream中定義的用於輸出的cout變量實際上是std::cout,而endl實際上是std::endl。因此,可以省略編譯指令using,以下述方式進行編碼:

std::cout << "Come up and C++ me some time.";
std::cout << std::endl;

然而,多數用戶並不喜歡將引入名稱空間之前的代碼(使用iostream.h和cout)轉換為名稱空間代碼(使用iostream和std::cout),除非他們可以不費力地完成這種轉換。於是,using編譯指令應運而生。下面的一行代碼表明,可以使用std名稱空間中定義的名稱,而不必使用std::前綴:

using namespace std;

這個using編譯指令使得std名稱空間中的所有名稱都可用。這是一種偷懶的做法,在大型項目中是一個潛在的問題。更好的方法是,只使所需的名稱可用,這可以通過使用using聲明來實現:

using std::cout;    // make cout available
using std::endl;    // make endl available
using std::cin;     // make cin available

用這些編譯指令替換下述代碼後,便可以使用cin和cout,而不必加上std::前綴:

using namespace std; // lazy approach, all names available

然而,要使用iostream中的其他名稱,必須將它們分別加到using列表中。本書首先採用這種偷懶的方法,其原因有兩個。首先,對於簡單程序而言,採用何種名稱空間管理方法無關緊要;其次,本書的重點是介紹C++的基本方面。本書後面將採用其他名稱空間管理技術。

2.1.6 使用cout進行C++輸出

現在來看一看如何顯示消息。myfirst.cpp程序使用下面的C++語句:

cout << "Come up and C++ me some time.";

雙引號括起的部分是要列印的消息。在C++中,用雙引號括起的一系列字符叫作字符串,因為它是由若干字符組合而成的。<<符號表示該語句將把這個字符串發送給cout;該符號指出了信息流動的路徑。cout是什麼呢?它是一個預定義的對象,知道如何顯示字符串、數字和單個字符等(第1章介紹過,對象是類的特定實例,而類定義了數據的存儲和使用方式)。

馬上就使用對象可能有些困難,因為幾章後才會介紹對象。實際上,這演示了對象的長處之一——不用了解對象的內部情況,就可以使用它。只需要知道它的接口,即如何使用它。cout對象有一個簡單的接口,如果string是一個字符串,則下面的代碼將顯示該字符串:

cout << string;

對於顯示字符串而言,只需知道這些即可。然而,現在來看看C++從概念上如何解釋這個過程。從概念上看,輸出是一個流,即從程序流出的一系列字符。cout對象表示這種流,其屬性是在iostream文件中定義的。cout的對象屬性包括一個插入運算符(<<),它可以將其右側的信息插入到流中。請看下面的語句(注意結尾的分號):

cout << "Come up and C++ me some time.";

它將字符串「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中第二個輸出流中看起來有些古怪的符號:

cout << endl;

endl是一個特殊的C++符號,表示一個重要的概念:重起一行。在輸出流中插入endl將導致螢幕光標移到下一行開頭。諸如endl等對於cout來說有特殊含義的特殊符號被稱為控制符(manipulator)。和cout一樣,endl也是在頭文件iostream中定義的,且位於名稱空間std中。

列印字符串時,cout不會自動移到下一行,因此在程序清單2.1中,第一條cout語句將光標留在輸出字符串的後面。每條cout語句的輸出從前一個輸出的末尾開始,因此如果省略程序清單2.1中的endl,得到的輸出將如下:

Come up and C++ me some time.You won』t regret it!

從上述輸出可知,Y緊跟在句點後面。下面來看另一個例子,假設有如下代碼:

cout << "The Good, the";
cout << "Bad, ";
cout << "and the Ukulele";
cout << endl;

其輸出將如下:

The Good, theBad, and the Ukulele

同樣,每個字符串緊接在前一個字符串的後面。如果要在兩個字符串之間留一個空格,必須將空格包含在字符串中。注意,要嘗試上述輸出示例,必須將代碼放到完整的程序中,該程序包含一個main()函數頭以及起始和結束花括號。

2.換行符

C++還提供了另一種在輸出中指示換行的舊式方法:C語言符號\n:

cout << "What’s next?\n";       // \n means start a new line

\n被視為一個字符,名為換行符。

顯示字符串時,在字符串中包含換行符,而不是在末尾加上endl,可減少輸入量:

cout << "Pluto is a dwarf planet.\n";       // show text, go to next line
cout << "Pluto is a dwarf planet." << endl; // show text, go to next line

另一方面,如果要生成一個空行,則兩種方法的輸入量相同,但對大多數人而言,輸入endl更為方便:

cout << "\n";   // start a new line
cout << endl;   // start a new line

本書中顯示用引號括起的字符串時,通常使用換行符\n,在其他情況下則使用控制符endl。一個差別是,endl確保程序繼續運行前刷新輸出(將其立即顯示在螢幕上);而使用「\n」不能提供這樣的保證,這意味著在有些系統中,有時可能在您輸入信息後才會出現提示。

換行符是一種被稱為「轉義序列」的按鍵組合,轉義序列將在第3章做更詳細的討論。

2.1.7 C++原始碼的格式化

有些語言(如FORTRAN)是面向行的,即每條語句占一行。對於這些語言來說,回車的作用是將語句分開。然而,在C++中,分號標示了語句的結尾。因此,在C++中,回車的作用就和空格或制表符相同。也就是說,在C++中,通常可以在能夠使用回車的地方使用空格,反之亦然。這說明既可以把一條語句放在幾行上,也可以把幾條語句放在同一行上。例如,可以將myfirst.cpp重新格式化為如下所示:

#include <iostream>
    int
main
() {    using
    namespace
         std; cout
            <<
"Come up and C++ me some time."
;    cout <<
endl; cout <<
"You won』t regret it!" <<
endl;return 0; }

這樣雖然不太好看,但仍然是合法的代碼。必須遵守一些規則,具體地說,在C和C++中,不能把空格、制表符或回車放在元素(比如名稱)中間,也不能把回車放在字符串中間。下面是一個不能這樣做的例子:

int ma in()     // INVALID -- space in name
re
turn 0;         // INVALID -- carriage return in word
cout << "Behold the Beans
 of Beauty!";  // INVALID -- carriage return in string

然而,C++11新增的原始(raw)字符串可包含回車,這將在第4章簡要地討論。

1.原始碼中的標記和空白

一行代碼中不可分割的元素叫作標記(token,參見圖2.3)。通常,必須用空格、制表符或回車將兩個標記分開,空格、制表符和回車統稱為空白(white space)。有些字符(如括號和逗號)是不需要用空白分開的標記。下面的一些示例說明了什麼情況下可以使用空白,什麼情況下可以省略:

return0;     // INVALID, must be return 0;
return(0);   // VALID, white space omitted
return (0);  // VALID, white space used
intmain();   // INVALID, white space omitted
int main()   // VALID, white space omitted in ()
int main () // ALSO VALID, white space used in ()

圖2.3 標記和空白

2.C++原始碼風格

雖然C++在格式方面賦予了您很大的自由,但如果遵循合理的風格,程序將更便於閱讀。有效但難看的代碼不會令人滿意。多數程式設計師都使用程序清單2.1所示的風格,它遵循了下述規則。

  • 每條語句占一行。
  • 每個函數都有一個開始花括號和一個結束花括號,這兩個花括號各占一行。
  • 函數中的語句都相對於花括號進行縮進。
  • 與函數名稱相關的圓括號周圍沒有空白。

前三條規則旨在確保代碼清晰易讀;第四條規則幫助區分函數和一些也使用圓括號的C++內置結構(如循環)。在涉及其他指導原則時,本書將提醒讀者。

2.2 C++語句

C++程序是一組函數,而每個函數又是一組語句。C++有好幾種語句,下面介紹其中的一些。程序清單2.2提供了兩種新的語句。聲明語句創建變量,賦值語句給該變量提供一個值。另外,該程序還演示了cout的新功能。

程序清單2.2 carrots.cpp

// carrots.cpp -- food processing program
// uses and displays a variable

#include <iostream>

int main()
{
    using namespace std;

    int carrots;              // declare an integer variable

    carrots = 25;             // assign a value to the variable
    cout << "I have ";
    cout << carrots;          // display the value of the variable
    cout << " carrots.";
    cout << endl;
    carrots = carrots - 1;    // modify the variable
    cout << "Crunch, crunch. Now I have " << carrots << " carrots." << endl;
    return 0;
}

空行將聲明語句與程序的其他部分分開。這是C常用的方法,但在C++中不那麼常見。下面是該程序的輸出:

I have 25 carrots.
Crunch, crunch. Now I have 24 carrots.

下面探討這個程序。

2.2.1 聲明語句和變量

計算機是一種精確的、有條理的機器。要將信息項存儲在計算機中,必須指出信息的存儲位置和所需的內存空間。在C++中,完成這種任務的一種相對簡便的方法,是使用聲明語句來指出存儲類型並提供位置標籤。例如,程序清單2.2中包含這樣一條聲明語句(注意其中的分號):

int carrots;

這條語句提供了兩項信息:需要的內存以及該內存單元的名稱。具體地說,這條語句指出程序需要足夠的存儲空間來存儲一個整數,在C++中用int表示整數。編譯器負責分配和標記內存的細節。C++可以處理多種類型的數據,而int是最基本的數據類型。它表示整數——沒有小數部分的數字。C++的int類型可以為正,也可以為負,但是大小範圍取決於實現。第3章將詳細介紹int和其他基本類型。

完成的第二項任務是給存儲單元指定名稱。在這裡,該聲明語句指出,此後程序將使用名稱carrots來標識存儲在該內存單元中的值。carrots被稱為變量,因為它的值可以修改。在C++中,所有變量都必須聲明。如果省略了carrots.cpp中的聲明,則當程序試圖使用carrots時,編譯器將指出錯誤。事實上,程式設計師嘗試省略聲明,可能只是為了看看編譯器的反應。這樣,以後看到這樣的反應時,便知道應檢查是否省略了聲明。



為什麼變量必須聲明?

有些語言(最典型的是BASIC)在使用新名稱時創建新的變量,而不用顯式地進行聲明。這看上去對用戶比較友好,事實上從短期上說確實如此。問題是,如果錯誤地拼寫了變量名,將在不知情的情況下創建一個新的變量。在BASIC中,ss程式設計師可能編寫如下語句:

CastleDark = 34
...
CastleDank = CastleDark + MoreGhosts
...
PRINT CastleDark

由於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表示的內存單元:

carrots = 25;

符號=叫作賦值運算符。C++(和C)有一項不尋常的特性——可以連續使用賦值運算符。例如,下面的代碼是合法的:

int steinway;
int baldwin;
int yamaha;
yamaha = baldwin = steinway = 88;

賦值將從右向左進行。首先,88被賦給steinway;然後,steinway的值(現在是88)被賦給baldwin;然後baldwin的值88被賦給yamaha(C++遵循C的愛好,允許外觀奇怪的代碼)。

程序清單2.2中的第二條賦值語句表明,可以對變量的值進行修改:

carrots = carrots - 1; // modify the variable

賦值運算符右邊的表達式carrots – 1是一個算術表達式。計算機將變量carrots的值25減去1,得到24。然後,賦值運算符將這個新值存儲到變量carrots對應的內存單元中。

2.2.3 cout的新花樣

到目前為止,本章的示例都使用cout來列印字符串,程序清單2.2使用cout來列印變量,該變量的值是一個整數:

cout << carrots;

程序沒有列印「carrots」,而是列印存儲在carrots中的整數值,即25。實際上,這將兩個操作合而為一了。首先,cout將carrots替換為其當前值25;然後,把值轉換為合適的輸出字符。

如上所示,cout可用於數字和字符串。這似乎沒有什麼不同尋常的地方,但別忘了,整數25與字符串「25」有天壤之別。該字符串存儲的是書寫該數字時使用的字符,即字符2和5。程序在內部存儲的是字符2和字符5的編碼。要列印字符串,cout只需列印字符串中各個字符即可。但整數25被存儲為數值,計算機不是單獨存儲每個數字,而是將25存儲為二進位數(附錄A討論了這種表示法)。這裡的要點是,在列印之前,cout必須將整數形式的數字轉換為字符串形式。另外,cout很聰明,知道carrots是一個需要轉換的整數。

與老式C語言的區別在於cout的聰明程度。在C語言中,要列印字符串「25」和整數25,可以使用C語言的多功能輸出函數printf():

printf("Printing a string: %s\n", "25");
printf("Printing an integer: %d\n", 25);

撇開printf()的複雜性不說,必須用特殊代碼(%s和%d)來指出是要列印字符串還是整數。如果讓printf()列印字符串,但又錯誤地提供了一個整數,由於printf()不夠精密,因此根本發現不了錯誤。它將繼續處理,顯示一堆亂碼。

cout的智能行為源自C++的面向對象特性。實際上,C++插入運算符(<<)將根據其後的數據類型相應地調整其行為,這是一個運算符重載的例子。在後面的章節中學習函數重載和運算符重載時,將知道如何實現這種智能設計。




關鍵字: