## 3.1 標準 C++精講
掌握標準 C++的基礎知識和技能是使用 Qt 進行編程的前提,雖然 Qt 也支持其他的語言 擴展(比如 Java、Python 等),但 Qt 的基礎和努力方向仍然是以 C++語言為主,所以讀者 朋友一定要掌握標準 C++。
### 3.1.1 程序設計語言介紹
1\.軟件
計算機內部所有能夠存儲的各種數據和能夠執行的各種程序都稱為軟件。而程序一詞 經常有兩種理解:(1)由程序員編寫的源代碼;(2)可執行的軟件。
程序通常可以分為以下幾類:
(1) 操作系統(Openation System)
為用戶管理計算機軟硬件的程序。例如 DOS、Windows 98、Windows XP、Windows CE、 UNIX、Linux、Mac OS X、BSD、Solaris 等。
實際上,操作系統包含很多可執行程序,這些程序組合在一起,完成一個或幾個特定 的任務。這些程序的根本目的在于有效的組織計算機的硬件資源,并為用戶提供一個訪問硬 件的友好界面。通常也可以將其稱為系統軟件。
(2) 應用軟件(Applications)
在操作系統下執行的,具有特殊用處的程序,如字處理軟件 Microsoft Word、游戲軟件、財務軟件等。這部分程序,也就是程序員主要的服務方向,也是軟件設計中商業利潤比較大的一部分內容。
(3) 應用程序開發環境(Application Development Environment)
協助程序員開發應用程序的特殊程序,如 Microsoft Visual Studio、Eclipse 等。
2\.程序語言
程序語言是人與計算機交流的工具。通過程序語言我們可以編寫程序,控制計算機執 行相應任務。到目前為止,先后出現了 4 代程序語言。
第 1 代語言:機器語言(machine language)
計算機執行的每一個操作都是由一組特定的二元指令所指定,稱為操作碼。通常將這些操作碼稱為機器語言。該語言不僅難于讀寫,撰寫復雜,而且非常容易出錯。
第 2 代語言:匯編語言(assembly language)
以接近英文和數學式的方式寫作程序,它的語意與機器語言有一對一的對應關系。該語言能發揮特定機器的硬件功能,編譯后的程序運行速度快、效率高,但因仍與機器硬件相關,編寫仍然較困難。
第 3 代語言:高級程序語言(high -level programming language)
包括 Fortran、Pascal、C、C++、Java、Basic、C#等,用這些語言編寫應用程序的時 候,通常不需要知道 CPU 執行的細節。這些語言都有自己的特色,能勝任某一方面的程序 設計。例如 Fortran 用于科學及工程的方程式運算,而 C 語言具備匯編語言的優點,可以 直接進行位運算,而且有高度的結構性,代碼相對容易維護,可理解性大大提高。 C++不僅 繼承了 C 的優點,而且引入了面向對象程序設計的思想。
第 4 代語言:特殊用途語言
使用在特殊的環境中,便于非專業程序設計人員使用的語言,例如 Perl、SQL、MATLAB 等,它們通常都不需要聲明變量,而且有很多現成的功能可以套用。
3\.編程思想
編程思想逐漸由結構化編程發展到面向對象編程。 結構化編程的主要思想是把問題細化,即把現實中的目標分解成一系列的任務,對每一個任務再進行分解,直到每個任務都能被解決為止。這是處理復雜問題的一種非常成功的方法。即使是現在,在處理很多問題上仍會經常用到這個解決問題的思想。當然,這種編程 思想存在著問題。首先,它將數據結構從函數中分離出來,這使得程序員在設計程序的時候不得不把一個事物的屬性與方法分離開考慮,于是就很難真實的表現現實生活中的模型。其 次,代碼的可重用性不高。雖然,現在用的很多函數都是用 C 語言編寫的,但是,現在需 要的可重用的代碼已經不再是這個層次上的代碼,而是集中了很多功能和數據的完整的組件,并且要保證其可維護。
面向對象的編程思想滿足以上的種種需求,它提供一種方法,可實現軟件組件的可重 用性,并將數據與操作數據的任務結合起來。
面向對象編程的實質就是將“對象”作為模型,而不是“數據”。用作模型的對象可 以是屏幕上的界面,如按鈕、列表框,也可以是現實世界中的對象,如自行車、人等。
面向對象的編程思想必須要涉及到封裝、繼承、多態性這 3 種技術,C++完全支持它們。
(1) 封裝
實際的事物有很多組成部分,描述一個事物也可以從不同的角度出發,從程序設計的 角度出發,需要兩個元素-數據和函數,當從這兩個元素出發去描述一個事物時,會看到: 一個事物本身具有一些屬性,相應的存在一些行為能改變其中的一些屬性。要用程序中的對 象來代表這些實際的事物,于是,用數據代表事物的屬性,用函數反映事物的行為。這樣, 與該對象相關的數據和行為就被封裝在這個對象里了。
例如:一個人有膚色、身高、體重等屬性,有自己的說話、跑、跳等行為。但有時, 屬性并不是實際存在的一個事物特征的反映,它可能是一種人為抽象出來的狀態的反映,例 如一個人有靜止、說話、跑步、睡覺 4 種狀態,可以定義一個屬性來表示當前這個人是處 于什么狀態。假設這個人處于百米跑起跑前的靜止狀態,當發令槍響起,這個人開始跑(即 發出跑的動作),同時狀態屬性發生變化,由靜止狀態變為跑步狀態;還有其他的屬性也隨 著這個動作的發出而發生變化,由靜止狀態變為跑步狀態;還有其他的屬性也隨著這個跑的 動作的發出而發生變化,如心跳頻率加快、移動速度變快等。這個過程即反映了行為改變屬 性這一特征。
程序并不一定要完全模擬實際的事物。在系統分析時,希望設計結果能真實反映實際 問題,但是,也沒有必要對涉及到的所有的事物都建立模型。下面針對系統分析過程中可能 遇到的問題舉例。一個顧客染發,染發的動作會改變頭發的顏色屬性。顯然,染發的動作由 理發師發出,但在程序中是否真的由理發師發出這個動作還需要根據具體問題再進行分析。
I\. 當具體問題中理發師的存在僅僅完成一個染發的作用,那么要考慮理發師是否有存 在的必要,如果沒必要,就果斷的刪除這個對象,把染發的動作交給顧客自己來完成。
II\. 當具體問題是為了描述理發師的行為狀態變化,那么可能就把這個染發的動作交給 理發師來完成。
該例子也說明,行為交給哪個對象來完成,是需要就具體問題進行分析而得到的,關 于這個問題可以參看下列規則:
+ 如果行為影響或修改了一個對象,最好把該行為交給目標對象(而不是動作的發起 者 )。
+ 如果行為涉及到多個對象以及它們之間的關聯,從中找出處于中心地位的對象,將 行為將給該對象完成。
這兩個規則只是經驗的反映,經驗不能反映所有的情況,也有可能把行為交給其他對象來完成也是合理的,這需要根據實際情況進行判斷。
(2) 繼承
這是代碼重用的很有效的方法,新的類可以通過繼承原有類,并選擇性的增加或修改其中的屬性或行為,以達到利用原有類的目的。現介紹一下代碼重用的方式。
注意,類是與對象相關聯的概念,將在稍后介紹。
I\. 源代碼剪貼
最原始的形式,缺點很多。首先是復制或修改原有代碼可能會出錯,其次需要對源代碼有一定程度的了解。另外,存在嚴重的配置管理問題,人們幾乎無法跟蹤源代碼多次修改重用的過程。
II\. 源代碼包含
許多程序語言都提供了包含庫中源代碼的機制。使用這種重用形式時,配置管理問題有所緩解,因為修改了庫中源代碼之后,所有包含它的程序自然都必須重新編譯。
III\. 繼承
利用繼承機制重用類庫中的類時,無需修改已有的代碼,就可以擴充或具體化原有類,因此,基本上不存在配置管理的問題。
(3) 多態
允許使用相同的接口,與各種不同的派生類定義出來的對象交互,能夠產生正確的行為。
例如,中國人說漢語,美國人說英語。 “說”的動作相同,卻有著不同的內容-漢語和英語;對于不同類型的電視機,都可以使用相同的動作:單擊【播放】按鈕,開始播放電 視頻道,顯然不同類型的電視機顯示的方法會有所區別,但沒有必要關心這些,人們看電視的目的已經達到了。
### 3.1.2 C++語法基礎知識
1\.預處理知識
在閱讀本節之前,讀者最好看看 C++這方面的專著。這里只講一些常用的知識,并不對 C++作全面深入的講解。
C++程序由對象、函數、變量及其他組件組成。 從最簡單的程序講起:
```
#include <instream.h>
int main()
{
cout<<”Hello World\n”;
return 0;
}
```
這是一個 Console 程序,撰寫 Console 程序時需要注意:主程序為 main 可以使用 C Runtime 函數和不涉及 GUI 的 Win32 API 函數。撰寫 Console 程序是學習 C++的第一步。進入 Console 模式進行編程,使用 VC++ 6.0,操作步驟如下:
(1) 選擇【File】菜單中的【New】菜單項,在彈出的對話框中選擇【Projects】標 簽;再選擇“Win32 Console Application“程序,然后輸入工程名稱,單擊【OK】按鈕; 在接下來的對話框中,為了脫離 VC 提供的代碼支持,選擇“an empty project”,然后單 擊【Finish】按鈕,會得到一個不包含任何工程文件的工程。之后,要加入包含著主函數的 頭文件。
(2) 選擇【File】菜單中的【New...】子菜單,在彈出的對話框中選擇【Files】標 簽,再選擇“C++ Source File”,選中“Add to Project”項,選中(1)中的工程名 稱。接下來,確定文件的名稱,單擊【 OK】按鈕,于是就得到了需要的文件,同時可以在這 個空文件里鍵入上述代碼。
(3) 運行程序,它會彈出一個對話框提示沒有可執行程序,詢問是否創建可執行程序。 單擊【是】即可,然后 VC 開始編譯,鏈接目標文件。如果沒有意外的話,會得到一個 DOS 窗口,窗口內有如下的輸出:
```
Hello World
Press any key to Continue
```
最后一句話并不是源代碼反映的內容,這是由編譯器提供的,方便查看輸出內容的提
示。如果讀者想看程序真正的輸出結果,就進入命令行下(即以前的 DOS 窗口下),運行 該程序,讀者就可以看到 Hello World,而沒有其他的信息。
```
#include<iostream.h>
```
該語句的意思是:將文件包含到當前的文件中,符號 #是預處理 標志。
注意,每次啟動編譯器時,先運行預處理器。預處理器瀏覽源代碼,找到以 #開頭的 行,處理這些行,預處理的作用是改變源代碼的文本。結果生成一個新的源代碼文件-一個 通常看不到的臨時文件,但讀者可以指定編譯器保存它,這樣,讀者可以在感興趣的時候或 者需要的時候檢查它。編譯器不讀取最初的源代碼文件,它讀取預處理器輸出的結果并編譯 該文件,最終生成可執行程序。
初步介紹預處理作用如下,以便讀者了解預處理的概念:
I\. 包含另一個文件(通常是頭文件),為了引入需要的代碼。
II\. 定義符號,祈禱開關作用,可以根據機器的情況、操作系統的情況及用戶的需求來 決定哪部分代碼有效,例如,如果要在 Win32 的環境下編程,那么,就定義 Win32 這個符 號。
III\. 定義宏,簡化常用的數據,或者簡化復雜的函數聲明、定義的過程。
預處理的功能不止這些,以上是本書程序中略有涉及的內容。
正式的程序從 main 函數開始,每一個 C++程序都有一個 main 函數。函數是指能實現一 個或多個功能的代碼塊。通常函數是由其他函數調用或激活,而 main 屬于特殊情況。程序 在開始的時候自動調用 main。從本質上講,也是被調用,不過,那些都不是現在需要關心 的事情。
所有的函數都以左大括號開始,以右大括號結束。兩個大括號之間是函數體。 使用對象 cout 將一個字符串打印到屏幕上。最后返回 0。
這些就是一個 C++程序的基本輪廓。下面開始講解+的基本語法。 2.基本數據類型
在任何一臺計算機中,每種變量類型都占據一定量的內存單元。但并不是每種變量類 型占的內存大小在每臺計算機上都相同,一個整型變量在一臺機器上可能是 2 個字節,而在另一臺機器上可能是 4 字節,但對任意一臺機器而言,這個值是確定的。字符型變量通常只有 1 個字節。
在大多數計算機上,短整型是 2 個字節,長整型是 4 個字節,而整型可能是 2 個字節也可能是 4 個字節。編程語言并沒有對此做出精確的定義,它定義的是短整型必須小于或 等于整型的大小,整型必須小于或等于長整型的大小。
整型的大小是由讀者所使用的處理器( 16 位還是 32 位)和編譯器決定的。例如,在使 用 Visual C++4.0 及以上版本的 32 位 Intel x86 計算機上,整型為 4 字節。詳細的變量 類型說明如表 3-1 所示。
表 3-1 C++變量類型說明
| 類型 | 大小 | 值 |
| --- | --- | --- |
| bool | 1 字節 | true 或 false |
| unsigned short int | 2 字節 | 0~65535 |
| short int | 2 字節 | -32768~32767 |
| unsigned long int | 4 字節 | 0~4294967295 |
| long int | 4 字節 | -2147483648~2147483647 |
| int(16 位) | 2 字節 | -32768~32767 |
| int(32 位) | 4 字節 | -2147483648~2147483647 |
| unsigned int(16 位) | 2 字節 | 0~65535 |
| unsigned int(32 位) | 4 字節 | 0~4294967295 |
| char | 1 字節 | 256 個字符 |
| float | 1 字節 | 1.2e-38~3.4e38 |
| double | 8 字節 | 2.2e-308~1.8e308 |
3\.表達式和語句
在 C++中,語句用于控制程序的執行順序、計算表達式的值,或者什么都不做(空語 句)。
所有的 C++語句都以分號結尾,即使是空語句也是如此。 下面是典型的語句:
```
value = value2 – value4;
```
在 C++中任何一個計算值的操作都可稱為表達式,表達式總是能夠返回一個值。表達式可以簡化到 1,2,3 這樣的整數,也可以是 a+b 這樣的形式,重要的是它能返回一個值。
運算符是一種能使編譯器進行某項操作的符號。運算符作用于操作數,在 C++中所有操 作數都是表達式。
(1) 賦值運算符(=):使賦值運算符左邊的操作數的值改變為賦值運算符右邊的值。 例如:
```
value = 20;
```
(2) 數學運算符:加(+)、減(-)、乘(*)、除(/)、取模(%)。取模運算符(%)就是求整型除法的余數。
I\. 一些特殊的運算符:如,`+=`,`-=`,`*=`,`/=`,`%=`。 例如:
`a+=2;` 等價于 `a=a+2;`
`a-=2;` 等價于 `a=a-2;`
`a*=2;` 等價于 `a=a*2;`
`a/=2;` 等價于 `a=a/2;`
`a%=2;` 等價于 `a=a%2;`
II\. 單目運算符:如++、--等。
`a++;`和`++a;` 等價于 `a=a+1;`
`a--;`和`--a;` 等價于 `a=a-1;`
`x=a++;` 等價于兩步操作:先是 `x=a;`然后是 `a=a+1;`
`x=++a;` 等價于兩步操作:先是 `a=a+1;`然后是 `x=a;`
在復雜的表達式之中,多個運算符同時出現,不同的符號有不同的執行優先級。例如:a=8-3*2,對于該表達式,由于*號的優先級大于-號,所以先執行 3*2,然后執行 8-6,最后執行 a=2。可以通過添加括號()來改變運算順序。將原式改成 a=(8-3)*2,這時,先 執行 8-3,再執行 5*2,最后執行 a=10。
(3) 關系運算符:用來對兩個量進行比較的(等于、大于或小于)符號。每條關系語句 的值要么為真要么為假,每個表達式都可以按真假來求值。凡是數值運算結果為 0 的表達 式返回假,否則返回真,也就是返回 False 或 True。C++基本的關系運算符如表 3-2 所示。
表 3-2 關系運算符說明
| 名稱 | 運算符 | 例子 | 值 |
| --- | --- | --- | --- |
| 等于 | == | 1==1 | true |
| | | 1==2 | false |
| 不等于 | != | 1!=1 | false |
| | | 1!=2 | true |
| 大于 | > | 1>2 | false |
| | | 2>1 | true |
| 大于等于 | >= | 1>=2 | fasle |
| | | 1>=1 | true |
| 小于 | < | 1<2 | true |
| | | 2<1 | false |
| 小于等于 | <= | 1<=2 | true |
| | | 2<=1 | false |
(4) 邏輯運算符:用于判斷操作數之間邏輯關系的運算符, C++基本的邏輯運算符如表3-3 所示。
表 3-3 邏輯運算符
| 運算符 | 符號 | 例子 |
| --- | --- | --- |
| 與(AND) | && | 表達式 1&&表達式 2 |
| 或(OR) | || | 表達式 1||表達式 2 |
| 非(NOT) | ! | !表達式 |
(5) 條件運算符(?:)
條件運算符(?:)是 C++中唯一的三目運算符。其格式如下:
```
(表達式 1)?(表達式 2):(表達式 3)
```
它的意思是:如果表達式 1 的值為真,就返回表達式 2 的值;否則就返回表達式 3 的值。通常,這個值將賦給某個變量。
例如:
```
c=(a>b)?a:bj;
```
等價于
```
if(a>b)
{
c = a;
}
else
{
c=b;
}
```
4\.函數
函數實際上是能夠對數據進行處理并返回一個值的子程序。每個 C++程序都至少有一個 函數 main。當程序啟動時,系統自動調用 main 函數。main 函數可調用其他的函數,其中 一些函數還可以再調用其他函數。每個函數都有自己的名字,當程序讀到函數名時,程序執 行就轉到函數體。這個過程稱作“調用函數”。當函數執行完后,程序又跳回到函數名所 在行的下一行繼續執行。設計得好的函數能執行特定的易于了解的任務。對于復雜的任務, 應該將其分成多個函數來完成,這些函數可以被程序依次調用。
函數通常有兩種類型:用戶定義函數和內置函數。用戶定義函數是由用戶自己編寫的 函數。內置函數則是編譯器軟件包的一部分-由開發商提供給用戶使用。
(1) 函數的聲明
在使用函數時,必須先聲明再定義。聲明告訴編譯器該函數的名稱、返回值類型以及參數。定義則告訴編譯器該函數的功能是什么。如果不聲明,任何函數都不能被其他函數調用。函數的聲明又稱為函數原型。
有 3 種函數的聲明:
I\. 將函數原型寫在某個文件中,再用#include 將其包含到程序中
II\. 將函數原型寫到使用該函數的文件中
III\. 在函數被其他函數使用前定義該函數,這樣做時,函數定義將作為聲明。
實際上,大家使用的許多內置函數已經將它們的函數原型寫到了用 #include 包含在程序使用的頭文件內。對于讀者自己編寫的函數,必須包含該原型。函數原型也是一條語句,也就是說它以分號結尾。它由函數的返回值類型和函數標識組成。函數標識包括函數名和參 數列表。參數列表包含函數的所有參數及其類型的列表,這些參數由逗號分開。
函數原型與它的定義必須在返回類型和標識上完全相符。如果不相符,就會有編譯錯 誤。不過,請注意,函數原型中不必包含參數名,而只需要參數類型。例如,像這樣的函數 類型就完全合乎要求:
```
long Area(int,int);
```
這個原型聲明了一個函數 Area,它返回一個長整型變量,它有兩個參數均為整型。不過,雖然這種方式合法,但它不夠好。如果在原型中加入參數名,這個函數原型就會變得清 晰得多。例如:
```
long Area(int length,int width);
```
就可以比較清楚的看到函數的作用,參數的意義。所有的函數都有返回值,如果未明確聲明返回值類型,則系統自動默認為整型。不過,精確的聲明每個函數的返回值類型,包 括main,可使程序更清楚。
(2) 函數的定義
函數的定義由函數頭和函數體組成。函數頭與函數原型很像,只是其中的參數必須有名稱,而且函數頭不以分號結尾。
函數體是包含在一對大括號內的一組語句。 可以這樣使用函數:
```
int main()
{
int areaOfRect = Area(4,5);
cout<<areaOfRect;
}
```
函數可以調用其他函數,甚至調用自己。
5\.類和對象
類與對象的關系反映在現實生活中就像是概念與實體、抽象與具體的關系。例如:提 起自行車,一定能在腦海中形成一個概念-兩個輪子,可以用腳蹬的交通工具。當然,大家 不可能“騎著”這個腦海中的概念去工作,或上學,只能騎著一部真正客觀存在的自行車 去工作或上學。這就是類與對象的區別,就是說對象是類的個體實例。
程序通常是用來解決實際問題的,為了方便模擬現實存在的事物以及事物之間的關 系,需要建立這樣的類來模擬現實生活中相應的事物。例如:模擬貓捉老鼠的 過程,就需 要建立兩個類,一個模擬貓,一個模擬老鼠。貓有個動作是追趕,老鼠有個動作是逃跑。這 樣就可以動手模擬這個過程了。
要聲明一個類,需要使用 C++的關鍵字 class,然后列出類的成員變量和成員函數,注 意類的聲明要以分號結束,例如:
```
class Rect
{
int width;
int length;
void show();
int getArea();
};
```
聲明 Rect 時,并沒有為它分配內存。它只是告訴編譯器: Rect 是什么,它包含什么數據(width,length),能做什么(show(),getArea())。他還告訴編譯器 Rect 要占多大空 間,大家可以算一算,Rect 需要編譯器預留多大空間呢?答案是 8 個字節,width 和 length 各占 4 個字節,加起來是 8 個,系統不會為成員函數分配存儲空間。
在定義類的成員變量和成員函數時,還經常用到兩個關鍵字 public 和 private。 類的所有成員默認時均為私有。私有成員只能通過類本身提供的成員函數內訪問。公有成員則可以被該類的對象訪問。如上例的 Rect 類,在默認情況下,width 和 length 都是私有成員變量,所以如果你寫下了這樣的代碼,就會出錯:
```
Rect rect;
rect.width = 10;
rect.length = 10;
```
因為在默認情況下成員變量 width 和 length 都是私有的,如果想讓這兩個成員變量都能夠被訪問,可以將它們改變為共有的,聲明如下:
```
class Rect
{
public:
int width;
int length;
void show();
int getArea();
};
```
在 public 之后修飾的成員變量和函數都被定義為公有的。這樣,下列語句就是合法
的:
```
rect.show();
```
要定義類的成員函數,可以在類聲明函數的地方,也可以在類聲明體之外的地方,但
需要 include 包含類的聲明的文件。
```
#include”Rect.h”
int Rect::show()
{
cout<<width;
cout<<length;
}
```
類中的成員函數類似 C 函數的定義方式,不同的是,每個成員函數之前帶有 “類名”和::域作用符,以表示該成員函數是屬于哪個類的成員。
當然,私有的數據成員和函數并不是只有該類的成員函數才能訪問到,友元函數和友 元類也能訪問,例如:
class Rect
{
public:
int width;
int length;
void show();
int getArea();
friend void setRectWidth(Rect* pRect);
friend class Point;
};
這樣類 Point 和函數 setRectWidth 都可以對類 Rect 的私有成員變量和函數訪問了。
6\.類的繼承機制
C++中允許單繼承和多繼承。一個類可以根據需要生成派生類。派生類根據情況繼承了基類的方法,還可以定義新的方法。一個子類的每個對象包含有從父類那里繼承來的數據成
員以及自己所特有的數據成員。在 C++語言中,派生類可以從一個基類派生,稱為單繼承; 也可以從多個基類派生,就是所謂多繼承。
派生類的繼承方式有“公有繼承(public)”、“私有繼承(private)”和“保護繼 承(protected)”這 3 種常見的方式,此外還有虛繼承這種方式。
這里重點介紹一下公有繼承。 公有繼承的特點是基類的公有成員和保護成員作為派生類的成員時,它們都保持原有的狀態,而基類的私有成員仍然是私有的。
(1) 基類成員對其對象的可見性:公有成員可見,其他不可見。這里保護成員同于私有 成員;
(2) 基類成員對派生類的可見性:公有成員和保護成員可見,而私有成員不可見。這里 保護成員同于公有成員;
(3) 基類成員對派生類對象的可見性:公有成員可見,其他成員不可見。
所以,在公有繼承時,派生類的對象可以訪問基類中的公有成員;派生類的成員函數可以訪問基類中的公有成員和保護成員。例如:
```
class BaseClass
{
protected:
int x;
int y;
};
class DerivedClass:public BaseClass
{
void show(){cout<<x;cout<<y;}; //訪問基類中受保護的數據成員
};
```
本節講述了 C++程序設計的基本內容,簡單的講解了 C++中實現面向對象程序設計思想
的語法、類與對象,這些只是 C++程序中很少的一部分,希望讀者能夠閱讀相關書籍加以鞏 固提高。
### 3.1.3 C++高級應用-虛函數
這里重點講一下虛函數。虛函數就是人們希望在派生類中被重新定義的函數,當我們
用基類的指針指向派生類的對象時,就能調用該派生類的虛函數。例如:
```
class BaseClass
{
public:
virtual void show(){};
};
class DerivedClass:public BaseClass
{
public:
int x;
int y;
void show(){cout<<x;cout<<y;};
};
```
函數 show()被基類聲明為虛函數,那么在派生類中也就都是虛函數。使用虛函數的用
意是什么呢,請看:
```
BaseClass* pBaseObject = NULL;
DerivedClass DerivedObject;
pBaseObject = &DerivedObject;
pBaseObject->show();
```
運行后,屏幕上將顯示 x,y 的值。
該例中,先定義了一個基類的指針,又定義了一個派生類的 對象,接著,用基類的指 針指向派生類的對象,最后用基類的指針調用函數 show(),這時,結果將輸出 x,y 的值。
從結果上可以看出,基類的指針調用了派生類的成員函數。
需要注意以下幾點: 1.在基類中聲明一個成員函數為虛函數后,在它的派生類中此成員函數也是虛函數,
并且不需要在前面加關鍵字。
2\.當指針調用函數時,如果調用的是虛函數,則根據指針指向的對象訪問函數;如果 調用的是非虛函數,則指針的類型調用相應的函數;如果虛函數在派生類中沒有定義,則會 自動調用基類中的函數定義。
另外,虛函數的使用需要謹慎,因為它會增加一些額外的開銷,不過這點開銷不足以 削弱它的強大功能,除非用戶漫無目的濫用它。
此外,可以將一個虛函數聲明為一個純虛函數:
```
virtual void show()=0;
```
這么做,等于告訴編譯器在對象中為函數 show 保留一個間隔,為將來從基類中派生的
函數占據一個位置。純虛函數有以下特點:
1\.純虛函數僅僅是用來為基類的派生類中的函數保留位置
2\.純虛函數在基類中沒有定義,它們被初始化為 0
3\.當虛函數變成純虛函數時,任何派生類都必須給出它自己的定義。否則編譯出錯。 在使用中,不能創建一個帶有純虛函數的類的對象,但允許聲明含有純虛函數的類的指針。 在程序中往往會用到這個功能。
- 第 1 章 走近 Qt
- 1.1 Qt 簡介
- 1.2 Qt 紀事概覽
- 1.3 Qt 套件的組成(以 Qt4.5 為準)
- 1.4 Qt 的授權
- 1.5 Qt 的產品
- 1.6 Qt 的服務與支持
- 1.7 Qt 的最新進展
- 1.8為什么選擇 Qt
- 1.9 問題與解答
- 1.10 總結與提高
- 第 2 章 Qt 的安裝與配置
- 2.1 獲取 Qt
- 2.2 協議說明
- 2.3 安裝 Qt
- 2.4 配置 Qt4 環境
- 2.5 問題與解答
- 2.6 總結與提高
- 第 3 章 Qt 編程基礎
- 3.1 標準 C++精講
- 3.2 Windows 編程基礎
- 3.3 Linux 編程基礎
- 3.4 Mac 編程基礎
- 3.5 問題與解答
- 3.6 總結與提高
- 第 4 章 Qt 4 集成開發環境
- 4.1 常見的 Qt IDE
- 4.2 Qt Creator
- 4.3 Eclipse
- 4.5 問題與解答
- 4.6 總結與提高
- 第 5 章 使用 Qt 基本 GUI 工具
- 5.1 使用 Qt Designer 進行 GUI 設計
- 5.2 使用 Qt Assistant 獲取在線文檔與幫助
- 5.3 使用 Qt Demo 學習 Qt 應用程序開發
- 5.4 問題與解答
- 5.5 總結與提高
- 第 6 章 Qt 4 程序開發方法和流程
- 6.1 開發方法
- 6.2 Hello Qt
- 6.3 幾個重要的知識點
- 6.4 問題與解答
- 6.5 總結與提高
- 第 7 章 對話框
- 7.1 QDialog 類
- 7.2 子類化 QDialog
- 7.3 快速設計對話框
- 7.4 常見內建(built in)對話框的使用
- 7.5 模態對話框與非模態對話框
- 7.6 問題與解答
- 7.7 總結與提高
- 第 8 章 主窗口
- 8.1 主窗口框架
- 8.2 創建主窗口的方法和流程
- 8.3 代碼創建主窗口
- 8.4 使用 Qt Designer 創建主窗口
- 8.5 中心窗口部件專題
- 8.6 Qt4 資源系統專題
- 8.7 錨接窗口
- 8.8 多文檔
- 8.9 問題與解答
- 8.10 總結與提高
- 第 9 章 Qt 樣式表與應用程序觀感
- 9.1 應用程序的觀感
- 9.2 QStyle 類的使用
- 9.3 樣式表概述
- 9.4 使用樣式表
- 9.5 問題與解答
- 9.6 總結與提高
- 第 10 章 在程序中使用.ui 文件
- 10.1 uic 的使用
- 10.2 Ui_YourFormName.h 文件的組成
- 10.3 編譯時加入處理.ui 文件的方法
- 10.4 運行時加入處理.ui 文件的方法
- 10.5 信號與槽的自動連接
- 10.6 問題與解答
- 10.7 總結與提高 本章主要講解了以下內容:
- 第 11 章 布局管理
- 11.1 基本概念和方法
- 11.2在 Qt Designer 中使用布局
- 11.3 基本布局實踐
- 11.4 堆棧布局
- 11.5 分裂器布局
- 11.6 自定義布局管理器
- 11.7 布局管理經驗總結
- 11.8 問題與解答
- 11.9 總結與提高
- 第 12 章 使用 Qt Creator
- 12.1 Qt Creator 概覽
- 12.2 Qt Creator 的組成
- 12.3 快捷鍵和常用技巧
- 12.4 Qt Creator 構建系統的設置
- 12.5 處理項目間依賴關系( Dependencies )
- 12.6 Qt 多版本共存時的管理
- 12.7 使用定位器在代碼間快速導航
- 12.8 如何創建一個項目
- 12.9 實例講解
- 12.10 使用 Qt Creator 調試程序
- 12.11 問題與解答
- 12.12 總結與提高
- 第 13 章 Qt 核心機制與原理
- 13.1 Qt 對標準 C++的擴展
- 13.2 信號與槽
- 13.3 元對象系統
- 13.4 Qt 的架構
- 13.5 Qt 的事件模型
- 13.6 構建 Qt 應用程序
- 13.7 總結與提高
- 附錄 A qmake 使用指南
- A.1 qmake 簡介
- A.2 使用 qmake
- 附錄 B make 命令
- B.1 命令解釋
- B.2 使用 make 自動構建
- 附錄 C Qt 資源
- C.1Qt 官方資源
- C.2 Qt 開發社區