- 現代C++編程:從入門到實踐
- (美)喬什·洛斯皮諾索
- 4075字
- 2024-04-15 11:40:39
1.3 開始認識C++
這一節會提供足夠多的信息來支持后面章節中的例子。關于這些細節你肯定會有疑問,但是接下來的章節會一一回答,不要慌!
1.3.1 C++類型系統
C++是一門面向對象的語言。對象是關于狀態和行為的抽象。想象一下真實世界的對象,例如電燈開關。我們可以把開關的各種屬性描述成狀態,例如:它開著還是關著?額定電壓是多少?在哪個房間?我們也可以描述開關的行為,例如:它是否從一個狀態(開)切換到另一個狀態(關)?是不是一個變光開關,在開和關之間還有很多別的狀態?
行為和狀態的集合被用來描述對象,我們稱之為類型。C++是一門強類型語言,這意味著每一個對象都有一個預先定義好的數據類型。
C++有一個內建整數類型,即int。int對象可以存儲整數(狀態),并且也支持許多數學運算(行為)。
要使用int類型來做一些有意義的任務,需要創建一些int對象,并且對它們命名。命名對象被稱為變量。
1.3.2 聲明變量
要聲明變量,我們需要先提供類型,接著提供名稱,然后以分號結束。以下例子聲明了一個名為the_answer的int變量:

類型int?后面便是變量名,即the_answer?。
1.3.3 初始化變量的狀態
當聲明變量時,我們同時會初始化它們。對象初始化會建立對象的初始狀態,例如設置它的值。我們將在第2章深入研究初始化的細節。目前,我們可以在變量聲明時在變量名后用等號(=)來設置變量的初始值。例如,我們可以用一行代碼實現變量the_answer的聲明和賦值:

這行代碼執行之后,我們會得到一個名為the_anwser的變量,其類型是int,初始值是42。當然,我們也可以將數學表達式的結果設置為變量的值,例如:

這行代碼會對表達式the_anwser / 6求值,然后把結果賦給lucky_number。int類型還支持許多其他運算,例如加法(+)運算、減法(-)運算、乘法(*)運算和模(%)運算。
注意 如果你不熟悉模運算或者好奇兩個數相除有余數時會發生什么的話,那么你問對了問題。這些問題將在第7章得到詳細的解答。
1.3.4 條件語句
條件語句允許在程序中做決定。這些決定取決于布爾表達式,布爾表達式的結果為真(true)或假(false)。例如,我們可以使用比較運算符,如“大于”或者“不等于”來構造布爾表達式。
一些和int有關的基本比較運算符見代碼清單1-2的程序。
代碼清單1-2 使用比較運算符的程序

這個程序不產生輸出(可以編譯和運行代碼清單1-2的程序來驗證這一點)。盡管這個程序不產生任何輸出,但編譯它可以讓我們檢驗C++程序是否合法。為了生成一些更有趣的程序,可以使用條件語句,如if語句。
if條件語句包含一個布爾表達式以及一條或多條嵌套語句。根據條件到底是真還是假,程序可以選擇執行哪一條嵌套語句。if語句的形式有好幾種,但是基本用法如下:

如果布爾表達式?為真,則嵌套語句?會執行;否則不執行。
有時候,我們需要運行一組語句而不是單條語句。這樣的語句組稱為復合語句。要聲明復合語句,可以直接將這一組語句包含在大括號{}中。if語句中可以使用復合語句,例如:

如果布爾表達式?為真,則復合語句?中的所有語句都會執行;否則都不執行。
我們也可以用else if和else語句來擴充if語句。使用它們可以描述更復雜的分支行為,如代碼清單1-3所示。
代碼清單1-3 帶有else if和else分支的if語句

首先,對boolean-expression-1?求值。如果boolean-expression-1為真,則執行statement-1,然后整個if語句停止執行。如果boolean-expression-1為假,則對boolean-expression-2求值?,并且如果為真,則執行statement-2,否則執行statement-3?。請注意,statement-1、statement-2和statement-3是互斥的,它們一起覆蓋了if語句所有可能的輸出,只有其中一個會被執行。
else if子句可以有零個或者多個。包括開始的if語句在內,每個else if的布爾表達式都會按順序求值。當其中一個布爾表達式為真時,就會停止求值,并執行相應的語句。如果沒有任何一個else if語句為真,則執行else子句的statement-3。和else if一樣,else也是可選的。
請考慮代碼清單1-4,它使用if語句來判斷打印哪條語句。
代碼清單1-4 具有條件行為的程序

編譯這段程序并且運行它。結果應該是Zero。如果改變x的值,這段程序會打印什么?
注意 代碼清單1-4中的main函數忽略了return語句。這是因為main函數是個特殊函數,return語句不是必需的。
1.3.5 函數
函數是一種代碼塊,它接受任意數量的輸入對象——稱為參數,同時可以將輸出對象返回給調用者。
我們可以按照代碼清單1-5中顯示的通用語法來聲明函數。
代碼清單1-5 C++函數的通用語法

函數聲明的第一部分是返回變量的類型?,比如int。當函數返回一個值時?,返回值的類型必須與返回類型return-type匹配。
在聲明返回類型之后,需要聲明函數的名稱?。函數名后的圓括號內包含函數所需的參數,它們是以逗號分隔的輸入參數。每個參數都有一個類型和一個名稱。
代碼清單1-5中的函數有兩個參數。第一個參數的類型為par-type1,名稱為par_name1?;第二個參數的類型為par-type2,名稱為par_name2?。參數代表傳遞給函數的對象。
后面的大括號內是函數體。這是一條復合語句,其中包含了函數的邏輯。在這個邏輯中,函數可能會決定向調用者返回一個值。返回值的函數可以有一條或多條return語句。一旦函數返回,它就停止執行,程序流程回到調用該函數的地方。我們來看一個例子。
1.示例:階躍函數
出于演示的目的,這里將展示如何構建數學函數step_function,該函數對于所有負參數返回-1,對于零值參數返回0,對于所有正參數返回1。代碼清單1-6顯示了編寫step_function的方法。
代碼清單1-6 階躍函數,當參數為負數時返回-1,為零時返回0,為正數時返回1

step_function接受一個參數x?。result變量被聲明并且被初始化為0?。如果x小于0,if語句將result設置為-1?。如果x大于0,if語句就將result設置為1?。最后,result被返回給調用者?。
2.調用函數
要調用一個函數,需要使用函數的名稱、大括號,以及一系列逗號隔開的必需參數。編譯器會從頭到尾讀取文件內容,所以函數的聲明必須出現在它第一次被使用之前。
請考慮代碼清單1-7中的程序,它調用了step_function。
代碼清單1-7 一個調用step_function的程序(該程序沒有任何輸出)

代碼清單1-7調用了step_function三次,每次調用都使用不同的參數,結果分別賦給了變量value1、value2和value3。
如果能把這些值打印出來不是更好嗎?所幸我們可以使用printf函數來打印不同變量的輸出。技巧就是使用printf格式指定符。
1.3.6 printf格式指定符
除了打印字符串常量(如代碼清單1-1中的Hello, world!)以外,printf還可以將多個值組成一個格式良好的字符串。它是一種特殊的函數,可以接受一個或者多個參數。
printf的第一個參數一直是格式化字符串。格式化字符串為要打印的字符串提供模板,并且它可以包含任意數量的特殊格式指定符(format specifier)。格式指定符告訴printf如何解釋和格式化跟在格式化字符串后面的參數。所有格式指定符都以%開頭。
例如,int的格式指定符是%d。當printf在格式化字符串中看到%d時,它就知道格式指定符后面的參數是int參數。然后,printf就用參數的實際值來替換格式指定符。
注意 printf函數最初來自BCPL的writef,BCPL是Martin Richards于1967年設計的一門編程語言(已過時)。writef函數使用%H、%I和%O指定符,最終會通過WRITEHEX、WRITED和WRITEOCT函數來生成十六進制和八進制輸出。目前仍然不清楚%d來自哪里(可能是WRITED的D?),但是我們也只能用它了。
考慮以下printf調用,它將打印字符串Ten 10, Twenty 20, Thirty 30:

第一個參數"Ten%d, Twenty%d, Thirty%d"是格式化字符串。注意,這里有三個字符串指定符%d???。因此,也有三個參數跟在格式化字符串后面???。當printf構建輸出時,它會用?替換?,用?替換?,用?替換?。
iostream、printf和輸入/輸出教學法
人們對教給C++新手哪種標準輸出方法有非常強烈的意見。一種方法是printf,它的血統可以追溯到C語言。另一種方法是cout,它是C++標準庫的iostream庫的一部分。本書兩者都教:第一部分主要介紹printf,第二部分主要介紹cout。這就是原因。
本書將引導你逐漸構建C++知識體系。每一章都是按順序設計的,所以你不需要躍躍欲試地去理解代碼示例。你會清楚地知道每一行代碼在做什么。因為printf是相當初級的,所以學完第3章,你就有足夠的知識來了解它的具體工作原理。
相比之下,cout涉及一大堆C++概念,只有學完第一部分,才會有足夠的背景知識來理解它的工作原理(什么是流緩沖區?什么是operator<<?什么是方法?flush()是如何工作的?cout會在析構函數中自動刷新嗎?什么是析構函數?setf是什么?格式化標志又是什么?是BitmaskType嗎?什么是操縱符?等等)。
當然,printf也有問題,一旦你學會了cout,你應該會更喜歡它。使用printf很容易導致格式指定符和參數不匹配,進而導致奇怪的行為,還可能導致程序崩潰,甚至出現安全漏洞。使用cout意味著不需要格式化字符串了,所以也就不需要記住格式指定符了,因此永遠不會出現格式化字符串和參數不匹配的情況。iostream也是可擴展的,這意味著我們可以將輸入和輸出功能集成到自己的類型中。
本書直接教授現代C++,但在這個特殊的主題上,做出了一些妥協,這使得它看起來不那么現代,目的是使介紹過程更直接順暢。作為一個附帶的好處,你會碰到printf指定符,這很可能在你編程生涯的某個階段發生。大多數語言,如C、Python、Java和Ruby,都有printf指定符,C#、JavaScript等語言也有類似的功能。
1.3.7 重新審視step_function
我們來看另一個調用step_function的例子。代碼清單1-8包含了變量聲明、函數調用和printf格式指定符。
代碼清單1-8 打印對幾個整數調用step_function的結果的程序


因為程序使用了printf,所以包含了cstdio?。step_function?被定義了,這樣我們就可以在程序的后面使用它,而main?建立了定義的入口點。
注意 本書中的一些代碼清單是相互依存的。為了節約空間,常使用--snip--符號來表示對復用的部分不做任何修改。
在main中,我們初始化一些int類型,如num1?。接下來,我們將這些變量傳遞給step_function,并初始化結果變量以存儲返回的值,如result1?。
最后,調用printf來打印返回的值。每個調用都以一個格式化字符串開始,如"Num1:%d, Step:%d\n"?。每個格式化字符串中都嵌入了兩個格式指定符%d。根據printf的要求,格式化字符串后面有兩個參數,即num1和result1,它們分別對應這兩個格式指定符。
1.3.8 注釋
注釋是人類可讀的內容,它可以放到源代碼中。在代碼中添加注釋時,使用的符號是//或/**/。//告訴編譯器要忽略從第一個斜杠到下一個換行符間的所有內容,這意味著可以把注釋和代碼放在一起,也可以把注釋放在單獨的行中。

注釋以/*開頭,以*/結尾(斜杠之間的星號是可選的,但通常都會使用)。什么時候使用注釋是一個永遠爭論不休的問題。一些編程專家認為,代碼應該具有很強的表現力和自我解釋能力,因而注釋在很大程度上是沒有必要的。他們認為描述性的變量名、簡短的函數和良好的測試通常就是所需的所有文件。有些程序員則很喜歡用注釋。
你可以培養自己的注釋習慣。編譯器將完全無視你所做的一切,因為它從不解釋注釋。
- 大學計算機基礎(第二版)
- HoloLens Beginner's Guide
- JavaScript 網頁編程從入門到精通 (清華社"視頻大講堂"大系·網絡開發視頻大講堂)
- Hands-On Reinforcement Learning with Python
- Mastering ROS for Robotics Programming
- 微信小程序全棧開發技術與實戰(微課版)
- Getting Started with Eclipse Juno
- HTML5權威指南
- C++ Fundamentals
- C語言程序設計教程
- Java自然語言處理(原書第2版)
- Spring Boot 2+Thymeleaf企業應用實戰
- Swift Essentials(Second Edition)
- Mastering PostgreSQL 11(Second Edition)
- Java無難事:詳解Java編程核心思想與技術