第2章 開始學習C++

說明

看《C++ Primer Plus》時整理的學習筆記,部分內容完全摘抄自《C++ Primer Plus》(第6版)中文版,Stephen Prata 著,張海龍 袁國忠譯。只做學習記錄用途。

2.1 進入C++

2.1.1 main() 函數

如下幾行程式碼構成了函數定義,該定義由兩部分組成:第一行 int main()函數頭,花括弧中包括的部分叫函數體。函數頭對函數與程式其他部分之間的介面進行了總結;函數體是指出函數應做什麼的電腦指令。每條完整的指令都稱為語句,所有語句都以分號結束。main() 被啟動程式碼調用,啟動程式碼是程式和作業系統之間的橋樑,在運行獨立的 C++ 程式時,通常從 main() 函數開始執行。如果編譯器到達 main() 函數末尾時沒有遇到返回語句,則默認 return 0,這條隱含的返回語句只適用於 main() 函數,不適用於其他函數。作業系統根據 main() 函數的返回值來判斷程式是否存在問題,通常的約定是:退出值為零則意味著程式運行成功,為非零則意味著存在問題。

int main()
{
    statements;
    return 0;
}

2.1.2 C++ 注釋

注釋是程式設計師為讀者提供的說明,通常用於解釋程式碼的用途,編譯器編譯時會忽略注釋。行注釋以雙斜杠 // 打頭,到行尾結束;塊注釋(也稱C-風格注釋)以 /* 開始,到 */ 結束,塊注釋可以跨越多行。

//我是行注釋,我只能待在雙斜杠兄弟的後面

/* 	我是塊注釋,我可以在這一行
 	也可以在這一行
 	還可以在這一行,但我不能跑到它們的外面  */

2.1.3 C++ 預處理器

預處理器在程式進行主編譯之前對源文件進行處理。有些 C++ 實現使用翻譯器程式將 C++ 程式轉換為 C 程式,這裡的翻譯器也是一種預處理器,但通常所說的預處理器一般用來處理名稱以 # 開頭的編譯指令。不必執行任何特殊的操作來調用預處理器,它會在編譯程式時自動運行。下面是一種典型的預處理器操作: #include 編譯指令導致 iostream 文件的內容隨源程式碼文件的內容一起被發送給編譯器。這個過程中,原始文件並沒有被修改,而是將源程式碼文件和 iostream 組合成一個複合文件,編譯的下一階段將使用該文件。

#include <iostream> 	//一條預處理器指令

2.1.4 頭文件名

iostream 這樣的文件叫做包含文件(include file,由於它們被包含在其他文件中),也叫頭文件(header file,由於它們被包含在文件起始處)。C 語言的傳統是:頭文件使用擴展名 h。C++用法對老式 C 的頭文件保留了擴展名 h,對部分 C 頭文件去掉擴展名 h 並在文件名前加上前綴 c,純粹的 C++ 頭文件則沒有擴展名。

2.1.5 名稱空間

名稱空間支援是一項 C++ 特性。當使用兩個已封裝好的庫,但它們都包含一個同名函數時(例如 wanda() 函數),編譯器將不知道應該使用哪個版本。為此,可以將兩個庫函數的定義放在不同的名稱空間中,例如廠商 Microflop Industries 將他們定義的 wanda() 函數放入一個名為 Microflop 的名稱空間中,這樣,其 wanda() 函數的全稱為 Microflop::wanda() ;同樣,廠商 Piscine 的 wanda() 函數的全稱為 Piscine::wanda() 。這樣,程式就可以使用名稱空間來區分不同的版本了。

當使用某名稱空間中的類、函數或變數時,有三種方式:

  1. 使用 using 編譯指令 一次性導入名稱空間中定義的所有名稱,這是一種偷懶的做法,在大型項目中會存在潛在的問題;
  2. 使用 using 聲明 逐條導入所需的名稱;
  3. 使用 名稱空間名::名稱 的形式直接使用目標名稱空間中的類、函數或變數,例如 std::cout << "hello world"
using namespace std; 	//using編譯指令導入std名稱空間中的所有名稱

using std::cout; 		//using聲明導入std名稱空間中的cout
using std::endl;		//using聲明導入std名稱空間中的endl
using std::cin;			//using聲明導入std名稱空間中的cin

使用上述前兩種方式之一導入所需名稱後,便可以直接使用相應的名稱而不必加上名稱空間前綴。

2.1.6 使用 cout 進行 c++ 輸出

這裡涉及到運算符重載,詳見後面章節。 endl\n 都表示換行符,一個差別是 endl 確保程式繼續運行前刷新輸出(將其立即顯示在螢幕上),而 \n 不能提供這樣的保證,這意味著在有些系統中,有時可能在輸入資訊後才會出現提示。

2.1.7 C++源程式碼的格式化

在 C++ 中,回車的作用和空格或製表符相同,可以在能夠使用回車的地方使用空格,反之亦然。這說明既可以把一條語句放在幾行上,也可以把幾條語句放在同一行上,雖然不太好看,但仍是合法的程式碼。一行程式碼中不可分割的元素叫做標記,空格、製表符和回車統稱為空白,通常,必須用空白將兩個標記分開。一般來說,有效但難看的程式碼不會令人滿意,如果遵循合理的風格,程式將更便於閱讀:

  • 每條語句佔一行。
  • 每個函數的開始花括弧以及結束花括弧獨自各佔一行。
  • 語句在應該縮進的地方縮進。

2.2 C++ 語句

2.2.1 聲明語句

聲明語句創建變數,指出了要存儲的數據類型和程式對存儲在這裡的數據使用的名稱,聲明語句通常導致編譯器為變數分配記憶體空間。

int carrots;

2.2.2 賦值語句

賦值語句給變數提供一個值(將值賦給存儲單元),符號 = 叫做賦值運算符,C++ 和 C 可以連續使用賦值運算符,此時賦值將從右向左進行。

carrots = 25;	//常見賦值
a = b = c = 25;	//連續賦值

2.3 其他 C++ 語句

cincout 的使用,類的簡介:描述了一種數據類型的全部屬性(包括可使用它執行的操作),對象是根據這些描述創建的實體。

2.4 函數

C++ 函數分兩種:有返回值的和沒有返回值的。

2.4.1 有返回值的函數

有返回值的函數將生成一個值,這個值可賦給變數或在其他表達式中使用。被調用的函數叫做被調用函數(called function),包含函數調用的函數叫做調用函數(calling function)。函數原型描述的是發送給函數的資訊和返回的資訊,函數定義則包含了函數的程式碼,C++程式應當在首次使用函數之前提供其原型,有兩種方式:一是在源程式碼中輸入函數原型、二是包含相應的頭文件。

double sqrt(double);	//函數原型
#include <cmath>		//包含頭文件

2.4.2 函數變體

有些函數需要多個參數,有些函數不接受任何參數,有些函數沒有返回值。

double pow(double, double); 	//需要多個參數
int rand(void);					//不接受任何參數
int rand();						//不接受任何參數,void可省略
void bucks(double);				//沒有返回值

在有些語言中,有返回值的函數被稱為函數;沒有返回值的函數被稱為過程子程式;C++ 與 C 一樣,這兩種變體都被稱為函數。

2.4.3 用戶定義的函數

使用自定義函數時,通常將函數原型放到 main() 定義之前,將函數定義放到 main() 的後面。函數定義包括函數頭和花括弧中的函數體

type functionname(argumentlist) 	//函數頭
{
    statements						//函數體
}

2.4.4 用戶定義的有返回值的函數

函數原型描述了函數介面,即函數如何與程式的其他部分交互;參數列表指出了何種資訊將被傳遞給函數;函數類型指出了返回值的類型。

2.4.5 在多函數程式中使用 using 編譯指令

讓程式能夠訪問名稱空間中類、函數或變數的方法有多種:

  • using 編譯指令 放在所有函數定義之前,使得文件中所有函數都能使用該名稱空間中的所有元素。
  • using 編譯指令 放在特定函數定義中,讓特定函數能使用該名稱空間中的所有元素。
  • 在特定函數定義中使用 using 聲明 ,讓特定函數能使用該名稱空間中的指定元素。
  • 在需要使用名稱空間中某元素時,直接使用前綴 名稱空間名::名稱 的形式。