虛函式 1 靜態聯編與動態聯編,引入虛函式

2021-09-06 03:20:09 字數 4775 閱讀 8054

在實際開發工作中,為提高**的重用性,編寫通用的功能模組,往往需要設計處理幾種不同物件的通用程式,如示例2.1所示。

示例清單2.1

#include "stdio.h"

#include "stdlib.h"

//定義函式指標型別displayinteger,指向返回值為void,引數列表為(const int)的函式

typedef  void( *displayinteger)(const int);

//定義函式,將數字以十進位制形式輸出,該函式型別與displayinteger匹配

void displaydecimal(const int number)

printf("the decimal value is %d\n",number);

//定義函式,將數字以八進位制形式輸出,該函式型別與displayinteger匹配

void displayoctal(const int number)

printf("the octal value is %o\n",number);

//定義函式,將數字以十六進製制形式輸出,該函式型別與displayinteger匹配

void displayhexadecimal (const int number)

printf("the hexadecimal value is %x\n",number);

定義通用的顯示數字函式

displayformat  displayinteger函式指標型別,實參可以是以上定義的

3個函式之一。通過傳遞不同的實參,將數字以各種格式輸出

number       準備輸出的數字

void displaynumber(displayinteger displayformat,const int number)

//呼叫以實參傳入的函式,以某種格式輸出整型數字

displayformat(number);

int main(int argc, char* argv)

int number=0;

//如果有數字形式的命令列引數,將其輸出,否則輸出0

if(argc>1)             

number=atoi(argv[1]);

//分別以3種格式將數字輸出

displaynumber(displaydecimal,number);

displaynumber(displayoctal,number);

displaynumber(displayhexadecimal,number);

return 0;

命令列c121.exe  50

的輸出結果:

the decimal value is 50

the octal value is 62

the hexadecimal value is 32

示例2.1中定義了乙個通用函式:void displaynumber(displayinteger displayformat, const int number)。

其功能是以各種格式顯示整型數字。只要傳遞適當的實參(函式位址),該函式就能很好地工作。如果客戶需求發生變化,例如增加二進位制格式的輸出,只要增加相應的功能函式,例如,void displaybinary (const int number)即可。而通用函式displaynumber()不必改動。顯然,函式指標displayinteger給該函式增添了靈性,使其得以通用。

其實,以上函式的通用性得益於c++的動態聯編功能,而函式指標不過是該功能的一種應用形式。

在c++編譯時,對於常規的函式呼叫,編譯器在函式的呼叫處插入函式的相對位址,程式執行時可以由函式的相對位址計算出函式的絕對位址,這樣函式可以被正確呼叫。這種在編譯時就確定函式位址的聯編過程叫做靜態聯編。動態聯編是指在程式編譯時,編譯器並不知道函式的相對位址,呼叫函式的相對位址只有在程式執行時才能確定。例如在示例2.1中的displaynumber()函式體內,編譯器並不知道displayformat(number)呼叫的函式位址,真正的位址是在執行時通過實參傳入的。

2.2  引入虛函式

看來,基於動態聯編的機制,使用函式指標就可以編寫出相對通用的程式模組。然而,我們早已開始了物件導向的程式設計,類成為封裝功能模組的基本單位。所以不僅需要對函式指標進行動態聯編,更需要對類指標進行動態聯編。幸運的是,c++的確為開發者提供了這一支援,它就是虛函式。

只要在類的非靜態成員函式前加關鍵字virtual,這一函式就是虛函式。編譯器對於虛函式採用動態聯編方式。

2.2.1  例項:定義虛函式

那麼,如何利用虛函式編寫處理多種物件的通用程式呢?為通俗地闡述這一問題,下面討論如何以虛函式的方式改寫示例2.1。

(1)定義乙個基類,名為cdispdecimal。該類封裝乙個整型資料成員number、乙個虛擬成員函式virtual displayformat()。該虛函式將成員number以十進位制格式輸出。

class cdispdecimal

public:

cdispdecimal(int i)

cdispdecimal()

virtual displayformat()

printf("the decimal value is %d\n",number);

protected:

int number;

(2)從基類cdispdecimal派生出兩個子類,名為cdispoctal、cdisphexadecimal。這兩個類都過載基類的虛函式displayformat(),分別將number以八進位制、十六進製制格式輸出。

class cdispoctal :public cdispdecimal

public:

cdispoctal(int i)

cdispoctal()

virtual displayformat()

printf("the octal value is %o\n",number);

class cdisphexadecimal: public cdispdecimal

public :

cdisphexadecimal(int i)

cdisphexadecimal()

virtual displayformat()

printf("the hexadecimal  value is %x\n",number);

因為編譯器對虛函式動態聯編,所以每個類的虛函式要能完成該類特有的功能,上面定義的3個類就是如此。注意,過載虛函式要求所有函式宣告完全一致,即函式名稱和形參列表都一致。否則等同於普通函式的過載,不能實現動態聯編。

2.2.2  例項:編寫通用函式

下面編寫通用函式,它的乙個形參是基類的指標,即cdispdecimal*。函式體內呼叫該基類的虛函式。

void displaynumber(  cdispdecimal* displayformat)

//呼叫以實參傳入物件指標的虛函式,以某種格式輸出整型數字

displayformat->displayformat();

從**上也許看不出這個函式有什麼通用的味道,但讀者不要忽略這樣乙個事實:c++編譯器可以直接將派生類的指標轉換為基類指標。這樣,呼叫該函式時,實參不僅可以是cdispdecimal物件的位址,也可以是cdisphexadecimal或dispoctal物件的位址。同時,虛函式採用動態聯編,函式體內呼叫的虛函式displayformat()並不一定是基類定義的,它將由實參決定,也可能是由cdisphexadecimal或dispoctal定義的。

2.2.3  例項:定義主函式

分別以cdispdecimal、cdispoctal、cdisphexadecimal物件的位址為實參呼叫通用函式displaynumber(),以3種格式輸出整數。

int main(int argc, char* argv)

cdispdecimal deci;

cdispoctal octa;

cdisphexadecimal hexa;

//如果有數字形式的命令列引數,將其輸出,否則輸出0

if(argc>1)             

/*因為這3個類都定義了int型的轉換建構函式,即以int 為引數的建構函式,

所以下面可以直接賦值,而無需過載「=」運算子*/

deci=atoi(argv[1]);

octa=atoi(argv[1]);

hexa=atoi(argv[1]);

//分別以3種格式將數字輸出

displaynumber(  &deci);  //將呼叫cdispdecimal類定義的虛函式displayformat()

displaynumber(  &octa);  //將呼叫cdispoctal類過載的虛函式displayformat()

displaynumber(  &hexa);  //將呼叫cdisphexadecimal類過載的虛函式displayformat()

return 0;

命令列c121.exe  50

的輸出結果:

the decimal value is 50

the octal value is 62

the hexadecimal value is 32

上例定義的3個類分別封裝了一種功能的實現,如果需要增加二進位制格式的輸出,只需再定義乙個派生類cdispbinary,不必改寫通用函式displaynumber()。

通過上例對虛函式的討論,我們對它的應用價值已經有了乙個深刻的認識。正如指標是c語言的靈魂,虛函式是c++的靈魂。mfc微軟基礎類庫廣泛應用了虛函式,增強其通用性。於是,利用mfc文件/檢視框架,就可以編寫出多種功能的應用程式。

靜態聯編與動態聯編

在c 中,多型性主要是通過函式過載實現的。過載函式是指程式中對同名函式進行呼叫時,編譯器會根據函式引數的型別和個數,決定該呼叫哪一段函式 來處理這個函式呼叫。這種把函式呼叫與適當的函式 相對應的動作,叫做聯編。聯編分為靜態聯編和動態聯編。在編譯階段決定執行哪個同名的被呼叫函式,稱為靜態聯編。在編譯階...

動態聯編與靜態聯編

首先,聯編是指乙個電腦程式的不同部分彼此關聯的過程。靜態聯編是指聯編工作在編譯階段完成的,這種聯編過程是在程式執行之前完成的,又稱為早期聯編。要實現靜態聯編,在編譯階段就必須確定程式中的操作呼叫 如函式呼叫 與執行該操作 間的關係,確定這種關係稱為束定,在編譯時的束定稱為靜態束定。靜態聯編對函式的選...

動態聯編與靜態聯編

include using namespace std class base 輸出為 f1 of base destructor base f1 of derive destructor base 說明 只要將基類的函式設定為虛函式,那麼所有覆蓋它的子類的函式也都是虛函式,而不需要再使用virtua...