C++面向對象(二):C++ 概述

C++面向對象:C++ 概述

會有點長,不過讀過就全學會嘍!!!!!!
會有點長,不過讀過就全學會嘍!!!!!!
會有點長,不過讀過就全學會嘍!!!!!!

1.C + + 的起源和特點

1.1 C + + 的起源

C + + 是美國貝爾實驗室的 Bjarne Stroustrup 博士在 C 語言的基礎上, 彌補了 C 語言 存在的一些缺陷,增加了面向對象的特徵, 於 1980 年開發出來的一種過程性與對象性結 合的程序設計語言。最初他把這種新的語言叫做“含類的 C”,到 1983 年才取名爲C + + 。
C 語言是 1972 年由 Dennis Richie 在貝爾實驗室設計的一個通用目的程序設計語言, 它的前身是 B 語言, 而 B 語言又是在繼承和發展了 BCPL 語言的基礎上設計的, C 最初用 作 UNIX 操作系統的描述語言。開發者希望它功能強、性能好,能像彙編語言那樣高效、 靈活, 又能支持結構化程序設計。由於這一追求的實現並隨着 UNIX 的成功和廣泛使 用, C 語言被介紹於世並立即贏得了青睞, 到了 80 年代已經廣爲流行, 成爲一種應用最廣 泛的程序設計語言。
但是 C 語言也存在着一些侷限:

  1. C 語言的類型檢查機制相對較弱, 這使得程序中的一些錯誤不能在編譯階段由 編譯器檢查出來。
  2. C 語言本身幾乎沒有支持代碼重用的語言結構。
  3. C 語言不適合開發大型程序, 當程序的規模達到一定的程度時, 程序員就很難控 製程序的複雜性。

C + + 正是爲了解決上述問題而設計的。C + + 繼承了 C 的原有精髓, 如高效率、靈 活性;擴充增加了對開發大型軟件頗爲有效的面向對象機制; 彌補了 C 語言不支持代碼 重用、不適宜開發大型軟件的不足, 成爲一種既可用於表現過程模型, 又可用於表現對象 模型的優秀的程序設計語言之一。

1.2 C + + 的特點

C + + 現在得到了越來越廣泛的應用, 它繼承了 C 語言的優點, 並有自己獨到的特 點,最主要的有:

  1. C + + 保持與 C 兼容, 這就使許多 C 代碼不經修改就可以爲 C + + 所用, 用 C 編 寫的衆多的庫函數和實用軟件可以用於 C + + 中。
  2. 用 C + + 編寫的程序可讀性更好, 代碼結構更爲合理, 可直接地在程序中映射問 題空間的結構。
  3. 生成代碼的質量高, 運行效率僅比彙編語言代碼段慢 10%到 20%。
  4. 從開發時間、費用到形成的軟件的可重用性、可擴充性、可維護性和可靠性等方 面有了很大的提高,使得大中型的程序開發變得更加容易。
  5. 支持面向對象的機制, 可方便地構造出模擬現實問題的實體和操作。 總之,目前人們對 C + + 的興趣越來越濃, 它已經成爲被廣泛使用的通用程序設計語 言。在國內外使用和研究 C + + 的人正迅猛增加, 優秀的 C + + 版本和配套的工具軟件 不斷湧現。

2.C + + 源程序的構成

2.1 C + + 程序的一般格式

C + + 是 C 的一個超集,它幾乎保留了 C 的所有的特性。下面我們給出一個簡單的 兩數相加 C + + 程序,以便讀者對 C + + 程序的格式有一個初步的瞭解。

// sum .cpp 
#include <iostream.h> 
int add(int a , int b) ; // 函數原型的說明 
int main () // 主函數 
{
	int x, y, sum; // 定義三個整型變量 
	cout << ”Enter two numbers:<<′\ n′; // 界面: 提示用戶輸入兩個數 
	cin >> x; // 從鍵盤輸入變量 x 的值 
	cin >>  y; // 從鍵盤輸入變量 y 的值 
	sum = add( x, y) ; // 調用函數 add,將得到的值賦給變量 q 
						// sum 
	cout << ”The sum is:<< sum <<′\ n′; 
	// 輸出兩個數的和 sum 的值 
	return 0 ; 
}
int add(int a , int b) // 定義 add 函數, 函數值爲整型 
{
	int c; // 定義一個整型變量 
	c = a + b; // 計算兩個數的和
	return c; // 將 c 的值返回, 通過 add 帶回調用處 q 
}
Enter two numbers: 
35↙
The sum is: 8

程序的第 1 行是 C + + 風格的註釋語句, 它由“/ / ”開始, 到行尾結束, 這條註釋語句 註明了本程序的文件名爲 sum .cpp。程序的第 2 行預編譯命令 # include < iostream .h > 的作用 是, 指 示 編 譯 程 序 把 頭 文 件 iostream .h 嵌 入 # include 命 令 所 在 的 源 程 序 中。 iostream .h 是 C + + 系統定義的一個頭文件, 它設置了 C + + 風格的 I/ O 環境。第 7 行 cout 語句的作用是將字符串“Enter two numbers:”在屏幕上顯示出來“, \ n”是換行符, 即 輸出上述信息後回車換行。第 8 行和第 9 行中的 cin 的作用是分別輸入變量 x 和 y 的值, 即執行 cin 後, 把從鍵盤輸入的兩個數值分別賦給變量 x 和 y。第 10 行用來調用 add 函 數, 調用時把實際參數 x 和 y 的值傳給函數 add( ) 中的形式參數 a 和 b, 執行 add 函數後得 到一個返回值(即 add( )函數中的 c) ,把這個值賦給 sum,然後第 11 行輸出 sum 的值。

本程序用來計算兩個整數的和。它由兩個函數組成: 主函數 main ( ) 和被調用函數 add( )。函數 add( )的作用是計算 a 與 b 的和, 把其值賦給變量 c。return 語句把 c 的值返 回給主函數 main( )。返回值通過函數名 add 帶回到 main( ) 函數的調用處。

2.2 C + + 程序的結構特點

通過以上例子,可以看出 C + + 程序的結構有以下特點:
( 1) C + + 程序通常由包括 main ( )在內的一組函數組成, 函數是構成 C + + 程序的基 本單位。其中名爲 main 的函數稱爲主函數, 可以將它放在程序的前部, 也可放在程序的 中部或後部。但是,不論主函數放在程序的什麼部位, 程序運行時第一個被執行的函數必 定是主函數。因此, 一個可運行的程序必須有主函數。被調用的函數可以是系統提供的 庫函數,也可以是用戶自己編寫的函數 ( 例如上面例子中的函數 add ( ) ) 。對於用戶自己 定義的函數,使用前應給予“聲明”, 如上面例子中的“int add(int a, int b) ;”。可以說 C + + 是函數式的語言,程序的全部操作都是由函數來完成的。
(2 ) C + + 函數由函數的說明部分和函數體兩部分組成。

  • ① 函數的說明部分
    這部分包括函數名、函數類型、函數 參數 ( 形式 參數 ) 及其類 型。 add( )函數的說明部分爲:

在這裏插入圖片描述
函數類型規定爲函數返回值的類型, 如 int、float 等。無返回值的函數是 void 類型。 main ( )函數是一個特殊的函數, 可看作是由操作系統調用的一個函數, 其返回值是 void 型或 int 型。當其返回值爲 int 型時, 可以使用 return 語句從 main( ) 中返回一個值, 正如 從其它函數中返回值一樣。在本例中,返回值爲零意味着 main( ) 已被成功地執行。
函數參數可有可無,但函數名後面的圓括號不能省略。

  • ② 函數體
    函數說明部分下面的花括號{……}內的部分稱爲函數體。如果一個函數內有多對花 括號,則最外層一對{ }爲函數體的範圍。函數體一般包括:
    a . 變量定義 如例 2 .1 主函數 main( )中的“int x, y,sum;”。
    b . 執行部分 由若干語句組成, 每個語句以分號結束。如例 2 .1 add ( )函數中的“c = a + b; return c;”等。
    在某些情況下,函數可以沒有變量定義部分, 甚至可以既無變量定義也無執行部分。
    例如:
    dump( ) { }
    這是一個空函數,不執行任何操作, 但是它是一個合法的函數。

(3 ) C + + 中每個語句和數據定義必須以分號結束。分號是 C + + 的必要組成部分, 即使程序中的最後一個語句也應是分號結束。由於用分號來區分每個語句, 因此幾乎可 以用任何格式來書寫 C + + 程序。一行內可以寫多個語句, 一個語句也可以分寫在多行 上。
說明:

  • (1 ) C 源程序文件擴展名爲 .c, 而 C + + 源程序文件擴展名爲 .cpp。
  • ( 2) 常用的 C + + 版本, 如 Turbo C + + 或 Borland C + + 都帶有 C 和 C + + 兩種編譯 器,當源程序文件擴展名爲 .c 時, 啓動 C 編譯器; 當源程序文件擴展名爲 .cpp 時, 啓動 C + + 編譯器。

3.C + + 在非面向對象方面的一些特性

C + + 是從 C 發展而來, C 程序中的表達式、語句、函數和程序的組織方法等在 C + + 中仍可以使用。C + + 對 C 語言注入了面向對象的新概念, 同時也增加了一些非面向對 象的新特性,這些新特性使 C + + 程序比 C 程序更簡潔或更安全。

3.1 註釋行

在 C 語言中,我們用“/ * ”及“ */ ”作爲註釋分界符號, 例如:
*/ * This is a test /
C + + 除保留了這種註釋方式外, 還提供了一種更有效的註釋方式, 該註釋以“/ / ”開 始,到行尾結束。例如以下兩條語句是等價的:
x = y + z; / * This is a comment */
x = y + z;/ / This is a comment
C + + 的“/ / …”註釋方式特別適合於註釋內容不超過一行的註釋, 這時, 它顯得很 簡潔。

說明:

  1. 以/ / 開始的註釋內容只在本行起作用。因此當註釋內容分爲多行時, 通常用/ * … */ 方式; 如果用/ / 方式,則每行都要以/ / 開頭。
  2. / * … */ 方式的註釋不能嵌套, 但它可以嵌套/ / 方式的註釋,例如:
 / * This is a multiline comment . 
 inside of which // is nested a single - line comment 
 Here is the end of the multiline comment . */

3.2 新的 I/ O 流

在 C 中進行 I/ O 操作時, 常會出現下面的錯誤:

int i; 
float f; 
scanf(% f”, i) ; 
printf(% d”, f) ;

在此,scanf( )和 printf( )所使用的格式控制符與輸出數據的類型不一致, 但 C 編譯器不能 檢查出這些錯誤。scanf( )的第二個參數應是一個指針參數, 但這樣的錯誤 C 編譯器也不 能檢查出來。
C + + 使用了更安全和更方便的方法來進行 I/ O 操作,如果使用 C + + 的 I/ O 操作, 上面的程序段可以寫爲:

int i; 
float f; 
cin > > i; 
cout < < f;

這裏的 cin 是標準的輸入流, 在程序中用於代表標準輸入設備, 即鍵盤。運算符“ > > ”在 C + + 中仍保持 C 中的“右移”功能, 但用於輸入時擴充了其功能, 表示將從標準輸入流 (即鍵盤) 讀取的數值傳送給右方指定的變量。請看下面的語句:
cin > > x;
此時,用戶從鍵盤輸入的數值會自動地轉換爲變量 x 的類型, 並存入變量 x 內。x 必須是 基本數據類型,而不能是 void 類型。

運算符“ > > ”允許用戶連續輸入一連串數據, 例如:
cin > > a > > b > > c;
它按書寫順序從鍵盤上提取所要求的數據, 並存入對應的變量中。兩個數據間用空白符 (空格、回車或 Tab 鍵 )分隔。


cout 是標準輸出流, 在程序中用於代表標準輸出設備, 通常指屏幕。運算符“ < < ”在 C + + 中仍保持 C 中的“左移”操作, 但用於輸出時擴充了其功能, 表示將右方變量的值寫 到標準輸出流 cout 中,即顯示在屏幕上。例如執行下面的語句後: **cout < < y;** 變量 y 的值將顯示在屏幕上。y 必須是基本數據類型,而不能是 void 類型。 運算符“ < < ”允許用戶連續輸出一連串數據, 也可以輸出表達式的值,例如: **cout < < a + b < < c;** 它按書寫順序將數據輸出到屏幕上。

說明:

  1. 使用 cincout 進行 I/ O 操作時, 在程序中必須嵌入頭文件 iostream .h, 否則編 譯時要產生錯誤。下面是一個完整的 C + + 程序。
# incluade < iostream .h > 
int main ( ) 
{ 
	char name[ 20 ] ; 
	cout < < ”Hello, your name:; 
	cin > > name; 
	cout < < name; 
	return 0; 
}
  1. 在 C + + 程序 中, 我們仍 然可以沿 用傳統的 stdio 函 數庫 中的 I/ O 函數, 如 **printf( )**函數、**scanf( )**函數或其它的 C 輸入輸出函數, 但只有使用“cin > > ”和“cout < < ”才 能顯示 C + + 的輸入和輸出風格。
  2. 使用“cin > >”可以連續輸入多個數據,但由於用戶常常忘記用空白符 (一個空格、 一個回車或一個 Tab 鍵 )來分隔兩個數值,容易造成輸入混亂, 因此使用時應加以注意。
  3. 前面用 coutcin 輸出輸入數據時, 全部使用了系統缺省的格式。實際上,我們 也可以對輸入和輸出格式進行控制。例如我們可用不同進制的方式顯示數據, 這時就要 用設置轉換基數的操縱符 dec、hex 和 oct。其中 dec 把轉換基數設置爲十進制, hex 把轉 換基數設置爲十六進制, oct 把轉換基數設置爲八進制, 缺省的轉換基數是十進制。請看 下面的例子
# include < iostream .h > 
void main( ) 
{
	int x = 25; 
	cout < < hex < < x < <′′< < dec < < x < <′′< < oct < < x < <′\ n′; 
}

此例執行的結果爲:

19 25 31

分別代表十六進制的 25、十進制的 25 及八進制的 25。

  1. 在 C 中, 常用′\ n′實現換行, C + + 中增加了換行控制符 endl, 其作用與′\ n′一 樣。例如以下兩個語句的操作是等價的:
cout < < ”x =< < x < < endl;
cout < < ”x =< < x < <′\ n′;

3.3 靈活的局部變量說明

在 C 語言程序中, 局部變量說明必須置於可執行代碼段之前, 不允許局部變量說明 和可執行代碼混合起來。例如在 C 中,下面的程序段是不正確的:

f( ) {int i; i = 10; int j; j = 25; / /}

因爲其中語句 i = 10 插在兩個變量說明之間, C 編譯指示有錯, 並中止對函數的編譯。但 在 C + + 中,以上程序段是正確的, 編譯時不會出錯。
此外, C + + 中允許在 for 循環語句中說明變量, 如:

for (int k = 5 ; k > = 0; k - - )

可見, C + + 允許在代碼塊中的任何地方說明局部變量, 它所說明的變量從其說明點 到該變量所在的最小分程序末的範圍內有效
通常認爲:在大函數中, 在最靠近使用變量的位置說明變量較爲合理; 而在較短的函 數中,把局部變量集中在函數開始處說明較好。

3.4 結構、聯合和枚舉名可直接作爲類型名

在 C + + 中,結構名、聯合名、枚舉名都是類型名。在定義變量時, 不必在結構名、聯 合名或枚舉名前冠以 struct、unionenum, 例如:

enum bool{FALSE, TRUE} ; 
struct string{ 
	char * ptr; int length; 
} ;

在定義變量時,可以說明爲:

bool done; 
string str; 

但是在傳統的 C 語言中,必須寫成:

 enum bool done; 
struct string str;

3.5 const 修飾符

在 C 中,習慣使用 # define 來定義常量, 例如:
# define LIMIT 100 ;
C + + 提供了一種更靈活、更安全的方式來定義常量, 即使用 const 修飾符來定義常 量,例如:
const int LIMIT = 100;
這個常量是類型化的,它有地址, 可以用指針指向這個值,但不能修改它。
const 也可以與指針一起使用,它們的組合情況較複雜, 可歸納爲三種:

  • 指向常量的指 針
  • 常指針
  • 指向常量的常指針

  1. 指向常量的指針是指一個指向常量的指針變量, 例如:
    const char * name = ”chen”; / / 聲明指向常量的指針
    這個語句的含義爲: 聲明一個名爲 name 的指針變量, 它指向一個字符型常量, 初始化 name 爲指向字符串”chen”。
    由於使用了 const ,不允許改變指針所指的常量, 因此以下語句是錯誤的:
    name[ 3] = ′a′;
    但是,由於 name 是一個指向常量的普通指針變量, 不是常指針, 因此可以改變 name 的 值。例如下列語句是允許的:
    name = ”zhang”;
    該語句賦給了指針另一個常量,即改變了 name 的值。
  2. 常指針是指把指針本身, 而不是它指向的對象聲明爲常量,例如:
    char * const name = ”chen”; / / 常指針
    這個語句的含義爲: 聲明一個名爲 name 的指針變量, 該指針是指向字符型數據的常指 針,用”chen”的地址初始化該常指針。 創建一個常指針,就是創建一個不能移動的固定指針, 但是它所指的數據可以改變, 例如:
    name[ 3] = ′a′; / / 合法
    name = ”zhang”; / / 出錯

    第一個語句改變了常指針所指的數據,這是允許的; 但第二個語句要改變指針本身, 這是 不允許的。
  3. 指向常量的常指針是指這個指針本身不能改變, 它所指向的值也不能改變。要 聲明一個指向常量的常指針,二者都要聲明爲 const, 例如:
    const char * const name = ”chen”; / / 指向常量的常指針
    這個語句的含義是:聲明瞭一個名爲 name 的指針變量,它是一個指向字符型常量的常指 針,用“chen”的地址初始化該指針。不難理解以下二個語句都是錯誤的:
    name[ 3] =′a′; / / 出錯, 不能改變指針所指的值 )
    name = ”zhang”; / / 出錯, 不能改變指針本身


說明:

  1. 如果用 const 定義的是一個整型常量, 關鍵字 int 可以省略。所以下面的兩行定 義是等價的: const int LIMIT = 100; const LIMIT = 100;
  2. 常量一旦被建立, 在程序的任何地方都不能再更改。
  3. 與 # define 定義的常量有所不同, const 定義的常量可以有自己的數據類型,這樣 C + + 的編譯程序可以進行更加嚴格的類型檢查, 具有良好的編譯時的檢測性。
  4. const 的作用與 # define 相似,但它消除了 # define 的不安全性, 因此建議用const 取代 # define 定義常量。
  5. 函數參數也可以用 const 說明, 用於保證實參在該函數內部不被改動, 大多數 C + + 編輯器能對具有 const 參數的函數進行更好的代碼優化。
  6. 雖然美國國家標準化協會 (ANSI) 制定的 C 標準 ( 簡稱 ANSI C 標準 ) 也採用了 const ,但兩者略有差別, 如下面的程序段在 C + + 中是合法的, 但卻不能被 ANSI C 所 接受
    const int size = 300;
    int a[size] ;

    另外,在 ANSI C 標準中, const 定義的常量是全局常量, 而 C + + 中 const 定義的常量 根據其定義的位置來決定其是局部的還是全局的。

3.6 內置函數

在函數說明前,冠以關鍵字“inline”, 該函數就被聲明爲內置函數。每當程序中出現 對該函數的調用時, C + + 編譯器使用函數體中的代碼替代函數調用表達式, 這樣能加快 代碼的執行,減少調用開銷。下面的程序定義了一個內置函數。
說明:

  1. 內置函數在被調用之前必須進行完整的定義, 否則編譯器將無法知道應該插入 什麼代碼。內置函數通常寫在主函數的前面。
  2. C + + 的內置函數具有與 C 中的宏定義 # define 相同的作用和相似的機理,但消 除了 # define 的不安全因素。
  3. 若內置函數較長,且調用太頻繁時, 程序將加長很多。因此, 通常只有較短的函 數才定義爲內置函數,對於較長的函數, 最好作爲一般函數處理。

3.7 函數原型

C 語言建議編程者爲程序中的每一個函數建立原型,而 C + + 要求必須爲每一個函 數建立原型,以說明函數的名稱、參數類型與個數, 以及函數返回值的類型。其主要目的 是讓 C + + 編譯程序進行檢查以確定調用函數的參數與事先定義的原型是否相符, 以及 返回值是否與原型相符,以維護程序的正確性。
函數原型的語法形式一般爲:

返回類型 函數名(參數表) ;

函數原型是一條語句,它必須以分號結束。它由函數的返回類型、函數名和參數表構 成。參數表包含所有參數及它們的類型,參數之間用逗號分開。請看下面的例子。

# include < iostream .h > 
void write( char * s) ; // 函數原型的說明 
int main( ) 
{ 
	write(”Hello, world !) ; 
} 
void write( char * s) 
{
	 cont < < s; 
 }

在程序中,要求一個函數的原型出現在該函數的調用語句之前。這樣, 當一個函數的 定義在後,而對它的調用在前時, 必須將該函數的原型放在調用語句之前; 但當一個函數 的定義在前,而對它的調用在後時, 一般就不必再單獨給出它的原型了。因爲, 這時函數 定義式的首部就起到了函數原型的聲明作用。


說明:

  1. 函數原型的參數表中可不包含參數的名字, 而只包含它們的類型。例如以下的 函數原型是完全合法的:
    long Area( int, int) ;
    該原型聲明一個返回類型爲 long、有兩個整型參數、名爲 Area 的函數。儘管這一結構是 合法的,但是加上參數名將使原型更加清楚。例如, 帶有參數名字的同一函數原型可以書 寫成:
    long Area( int length, int width ) ;

  2. 函數定義由函數說明部分和函數體構成。函數說明部分與函數原型基本一樣, 但函數說明部分中的參數必須給出名字,而且不包含結尾的分號, 例如:
    long Area( int length, int width ) { / / … return (length * width) ; }

  3. C + + 的參數說明必須放在函數名後的括號內,不可將函數參數說明放在函數說 明部分與函數體之間。因此編譯以下的程序將會出現“style of function definition is now obsolete”的錯誤信息:

  4. 主函數 main( ) 不必進行原型說明, 因爲它被看成一個自動說明原型的函數。主 函數是第一個被執行的函數,而且不存在被別的函數調用的問題。

  5. 原型說明中沒有指出返回類型的函數( 包括主函數 main ) , C + + 默認該函數的 返回類型是 int, 因此以下的原型說明在 C + + 中是等價的:
    cal( float a, int c ) ; / / 默認返回整數
    int cal( float a, int c ) ; / / 指明返回整數

  6. 如果一個函數沒有返回值, 則必須在函數原型中註明返回類型爲 void。如果主 函數沒有值可返回,可以在 main( ) 前註明 void,這樣主函數中就不必有“return 0;”之類的 返回語句了。真正的 ANSI C 標準是不允許定義一個 void main( )的。

  7. 如果函數原型中未註明參數, C + + 假定該函數的參數表爲空( void) 。例如以下 的原型說明在 C + + 中是完全一樣的:
    f( ) ; / / 表示該函數不帶任何參數
    f(void) ; / / 表示該函數不帶任何參數

    但是在 C 中,上述兩個原型說明是不同的:
    f(void) : / / 表示該函數不帶任何參數
    f( ) ; / / 表示該函數的參數信息沒有給出, 很可能它帶有多個參數

3.8 帶有缺省參數的函數

C + + 在說明函數原型時, 可爲一個或多個參數指定缺省參數值, 以後調用此函數 時,若省略其中某一參數, C + + 自動地以缺省值作爲相應參數的值。例如有一函數原型 說明爲:
int special(int x = 5 ,float y = 5 .3) ;
則 x 與 y 的缺省參數值分別爲 5 與 5 .3。

當進行函數調用時, 編譯器按從左向右順序將實參與形參結合, 若未指定足夠的實 參,則編譯器按順序用函數原型中的缺省值來補足所缺少的實參。例如以下的函數調用 都是允許的:

special(100, 79 .8) ; // x = 100, y = 79.8 
special(25) ; // x = 25 , y = 5.3 
special( ) ; // x = 5, y = 5.3

說明:

  1. 在函數原型中, 所有取缺省值的參數都必須出現在不取缺省值的參數的右邊。 亦即,一旦開始定義取缺省值的參數, 就不可以再說明非缺省的參數,例如:
    int fun(int i, int j = 5 , int k) ;
    它是錯誤的,因爲在取缺省參數的 int j = 5 後,不應再說明非缺省參數 int k。若改爲:
    int fun(int i, int k, int j = 5) ;
    則是正確的。
  2. 在函數調用時, 若某個參數省略,則其後的參數皆應省略而採用缺省值。不允許 某個參數省略後,再給其後的參數指定參數值。例如不允許出現以下調用 special( )函數 的語句: special( , 21 .5 ) ;

3.9 函數重載

在傳統的 C 語言中, 函數名必須是惟一的, 也就是說不允許出現同名的函數。當要 求編寫求整數、浮點數和雙精度數的平方數的函數時, 若用 C 語言來處理, 必須編寫三個 函數,這三個函數的函數名不允許同名。例如:

Isquare(int i) ; // 求整數的二次方 
Fsquare( float i) ; // 求浮點數的二次方 
Dsquare( double i) ; // 求雙精度數的二次方

當使用這些函數求某個數的平方數時,必須調用合適的函數, 也就是說, 用戶必須記住三 個函數,雖然這三個函數的功能是相同的。
在 C + + 中,用戶可以重載函數。這意味着, 只要函數參數的類型不同, 或者參數的 個數不同,或者二者兼而有之, 兩個或者兩個以上的函數可以使用相同的函數名。當兩個 或者兩個以上的函數共用一個函數名時,稱爲函數重載。被重載的函數稱爲重載函數。
由於 C + + 支持函數重載, 上面三個求二次方的函數可以起一個共同的名字 square, 但它們的參數類型仍保留不同。當用戶調用這些函數時,只需在參數表中帶入實參, 編譯 器就會根據實參的類型來確定到底調用哪個重載函數。因此, 用戶調用求二次方的函數 時,只需記住一個 square( )函數, 剩下的則都是系統的事情。

說明:

  1. 重載函數應在參數個數或參數類型上有所不同, 否則編譯程序將無法確定調用 哪一個重載版本,即使返回類型不同, 也不能區分。例如:
    int mul(int x, int y) ;
    double mul( int x, int y) ;
    雖然這兩個函數的返回類型不同,但參數個數和類型完全相同, 因此編譯程序將無法區分 這兩個函數。
  2. 一般而言, 重載函數應執行相同的功能, 例如 abs( ) 函數一般用來返回一個數的 絕對值,如果重載 abs( )函數, 讓它返回一個數的二次方根,則是不可取的。

3.10 作用域運算符“∷”

通常情況下,如果有兩個同名變量, 一個是全局的,另一個是局部的, 那麼局部變量在 其作用域內具有較高的優先權。

#include < iostream .h > 
int avar = 10; // 全局變量 
int main ( ) { 
	int avar; // 局部變量 
	avar = 25; 
	cout < < ”avar is”< < avar < < endl; 
	return 0; 
}

程序執行結果如下: avar is 25
此時,在 main( ) 函數的輸出語句中, 使用的變量 avar 是 main ( )函數內定義的局部變 量,因此打印的是局部變量 avar 的值。
如果希望在局部變量的作用域內使用同名的全局變量,可以在該變量前加上“∷”, 此 時∷avar 代表全局變量 avar“, ∷”稱爲作用域運算符。

# include < iostream .h > 
int avar; main ( ) 
{ 
	int avar; 
	avar = 25; / / 局部變量 
	avar ∷avar = 10; / / 全局變量 avar 
	cout < < ”local avar =< < avar < < endl; 
	cout < < ”global avar =< < ∷avar < < endl; 
	return 0; 
}

程序運行結果爲: local avar = 25 global avar = 10

3.11 無名聯合

無名聯合是 C + + 中的一種特殊聯合, 它在關鍵字 union 後面沒有給出聯合名, 它可 使一組數據項共享同一內存地址。如:

union {
	int i; 
	float f; 
}

在此無名聯合中,聲明瞭變量 i 和 f 具有相同的存儲地址。無名聯合可通過使用其中數據 項名字直接存取,例如可以直接使用上面的變量 i 或 f, 如:

i = 20 ;

3.12 強制類型轉換

在 C 中如果要把一個整數(int)轉換爲浮點數 (float) ,要求使用如下的格式:

 int i = 10; 
 float x = (float) i;

C + + 支持這樣的格式, 但還提供了一種更爲方便的函數調用方法, 即將類型名作爲函數 名使用,使得類型轉換的執行看起來好像調用了一個函數。上面的語句可改寫成:

  int i = 10;
   float x = float(i) ;

以上兩種方法 C + + 都能接受,但推薦使用後一種方式。

3.13 new 和 delete

C 語 言使用函數 malloc ( ) 和 free ( ) 動態 分配內存和釋放動態 分配的內 存。然 而 C + + 使用運算符 new 和 delete 能更好、更簡單地進行內存的分配和釋放。
運算符 new 用於內存分配的使用形式爲:

p = new type;

其中, type 是一個數據類型名, p 是指向該數據類型的指針。new 從稱爲堆的一塊自由存 儲區中爲程序分配一塊 sizeof( type )字節大小的內存,該塊內存的首地址被存於指針p 中。
運算符 delete 用於釋放 new 分配的存儲空間。它的使用形式一般爲:

delete p;

其中, p 必須是一個指針, 保存着 new 分配的內存的首地址。下面是 new 和 delete 操作的 一個簡單例子。

#include <iostream .h> 
int main() 
{
	int * p; // 聲明一個整型指針變量 
	p E p = new int ; // 動態分配一個 int 型存儲區,並將首地址賦給 p 
	*p = 10; 
	cout << *p; 
	delete p; // 撤消指針,釋放 p 指向的存儲空間 
	return 0; 
}

程序執行結果如下:

10

該程序定義了一個整型指針變量 p,然後用 new 爲其分配了一塊佔兩個字節的內存, p 指向這個內存塊。然後在這內存塊中賦予初值 10 ,並將其打印出來。最後撤消指針 p, 釋放 p 指向的存儲空間。

雖然 new 和 delete 完成的功能類似於 malloc ( ) 和 free( ) , 但是它們有以下幾個優點:

  1. new 可以自動計算所要分配內存的類型的大小, 而不必使用 sizeof( ) 來計算所需 要的字節數,這就減少了發生錯誤的可能性。
  2. new 能夠自動返回正確的指針類型,不必對返回指針進行類型轉換。
  3. 可以用 new 將分配的對象初始化。
  4. new 和 delete 都可以被重載,允許建立自定義的分配系統。

下面我們對 new 和 delete 的使用再作幾點說明:

  1. 使用 new 可以爲數組動態分配內存空間,這時需要在類型名後面綴上數組大小。 例如:
int * pi = new int[10] ;

這時 new 爲具有 10 個元素的整型數組分配了內存空間,並將首地址賦給了指針 pi。 使用 new 爲多維數組分配空間時, 必須提供所有維的大小,如:

int * pi = new int[2 ] [ 3] [4 ] ;

其中第一維的界值可以是任何合法的表達式,如:

int i = 3; 
int * pi = new int[i] [3 ] [ 4] ;
  1. new 可在爲簡單變量分配內存空間的同時,進行初始化。初始值放在“new type” 後面的圓括號內。請看下面的例子。
# include < iostream .h > 
int main() 
{
	int * p; 
	p = new int( 99 ) ; // 動態分配內存,並將 99 作爲初始值賦給它 
	cout << * p; 
	delete p; 
	return 0; }

但是, new 不能對動態分配的數組存儲區進行初始化。

  1. 釋放動態分配的數組存儲區時, 可使用如下的 delete 格式:
delete [ ] p; 

無須指出空間大小,但老版本的 C + + 要求在 delete 的方括號中標出數字,以告訴 C + + 要釋放多少個元素所佔的空間。

  1. 使用 new 動態分配內存時,如果沒有足夠的內存滿足分配要求, new 將返回空指 針( NULL) 。因此通常要對內存的動態分配是否成功進行檢查。請看以下例子
# include < iostream .h > 
int main() 
{
	int * p; 
	p = new int ; 
	if( ! p) 
	{ 
		cout < < ”allocation failure \ n”; 
		return 1;
	 }
	 * p = 20; 
	  cout < < * p; 
	  delete p;
	  return 0; 
  }

若動態分配內存失敗,此程序將在屏幕上顯示“allocation failure”。

3.14 引用

引用是 C + + 中的新概念, 引用是能自動間接引用的一種指針。自動間接引用的意 思是:不必使用間接引用運算符 * , 就可以得到一個引用值。引用可爲變量起別名, 它主 要用作函數參數以及函數的返回類型。

1 . 引用的定義
定義引用的關鍵字是“type &”, 它的含義是“type 類型的引用”, 此引用與 type 類型 的對象或變量的地址相聯繫。例如:

int i = 5; 
int &j = i;

它創建了一個整型引用, j 是 i 的別名, i 和 j 佔用內存同一位置。當 i 變化時, j 也隨之變 化,反之亦然。

  1. 定義引用時, 必須立即對它進行初始化,不能定義完成後再賦值。例如下述定義 是錯誤的:
nt i; 
int &j; // 錯誤 
j = i ;

爲引用提供的初始值,可以是一個變量或另一個引用。例如:

int i = 5; 
int &j1 = i; 
int &j2 = j1 ;

這樣定義後,變量 i 將有兩個別名: j1 和 j2。

  1. 引用實際上是一種隱式指針。每次使用引用的變量時, 可以不用書寫間接引用 運算符“ * ”,因而引用簡化了程序。
  2. 引用不可重新賦值, 不可使其作爲另一個變量的別名,例如:
int i, k; 
int &j = i; 
j = &k // 錯誤
  1. 引用不同於普通變量, 下面的類型聲明都非法的:
nt &b[3 ] ; // 不能建立引用數組 
int & * p; // 不能建立指向引用的指針
int & & r; // 不能建立引用的引用
  1. 當使用 & 運算符取一個引用的地址時, 其值爲所引用的變量的地址,例如:
int num = 50; 
int & ref = num; 
int * p = & ref;

則 p 中保存的是變量 num 的地址。
2 . 引用參數
C + + 提供引用, 其主要的一個用途就是將引用作爲函數的參數。

#include<iostream.h> 
void swap(int * m, int * n) 
{
	int temp; 
	temp = * m; 
	* m = * n ;
	 * n = temp; 
}
int main () 
{
	int a = 5, b = 10; 
	cout < < ”a =< < a < <”b =< < b < < endl; 
	swap( & a, &b) ; 
	cout < < ”a =< < a < <”b =< < b < < endl; 
	return 0 ; 
}

程序運行的結果爲:

a = 5 b = 10 
a = 10 b = 5

可見,採用按地址傳遞參數的方法, 調用函數 swap( )後, a 和 b 的值被交換了。
除了採用地址傳遞參數的方式外, C + + 又提供了採用“引用參數”傳遞函數參數的 方式,這是與上述方式性質完全不同的參數傳遞方式。

# include <iostream.h > 
void swap(int &m, int &n) 
{
	int temp; 
	temp = m;
	m = n; 
	n = temp; 
}
int main() 
{
	int a = 5, b = 10; 
	cout < < ”a =< < a < <”b =< < b < < endl; 
	swap( a , b) ; 
	cout < < ”a =< < a < <”b =< < b < < endl; 
	return 0 ; 
}

程序運行的結果爲:

a = 5 b = 10 
a = 10 b = 5

當程序中調用函數 swap( )時,實參 a 和 b 分別初始化引用 m 和 n, 所在函數 swap ( ) 中, m 和 n 分別引用 a 和 b, 對 m 和 n 的訪問就是對 a 和 b 的訪問,所以函數 swap( )改變 了 main( )函數中變量 a 和 b 的值。

儘管通過引用參數產生的效果同按地址傳遞是一樣的,但其語法更清楚簡單, 因爲在 調用函數的所有參數前不需要間接引用運算符 * ,原函數中傳送的參數也不必是引用變 量。C + + 主張用引用傳遞取代地址傳遞的方式, 因爲前者語法容易且不易出錯。
3 . 引用返回值
函數可以返回一個引用,將函數說明爲返回一個引用的主要目的是: 將該函數用在賦 值運算符的左邊

# include < iostream .h > 
int a[ ] = {1, 3, 5, 7, 9} ; 
int &index(int) ; // 聲明返回引用的函數
int main( ) 
{
	index (2 ) = 25; // 將 a [2 ]重新賦值爲 25 
	cout << index(2 ) ; 
}
int &index(int i) 
{ 
	return a [i] ; 
}

程序運行結果爲: 25
通常, 除了將函數定義爲返回引用類型, 一個函數是不能直接用在賦值運算符左邊 的。

在定義返回引用的函數時,注意不要返回對該函數內的自動變量( 局部變量)的引用。 例如:

int &fun()
{
	int a; // …
	return a; 
}

由於自動變量的生存期僅侷限於函數內部, 當函數返回時, 自動變量就消失了,因此上述 函數返回一個無效的引用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章